20260309:设备批量新增
This commit is contained in:
parent
2cfc9c35ea
commit
9536ae19b1
@ -21,6 +21,7 @@ import com.fastbee.iot.model.DeviceImportVO;
|
|||||||
import com.fastbee.iot.model.DeviceRelateUserInput;
|
import com.fastbee.iot.model.DeviceRelateUserInput;
|
||||||
import com.fastbee.iot.model.SerialNumberVO;
|
import com.fastbee.iot.model.SerialNumberVO;
|
||||||
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
||||||
|
import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO;
|
||||||
import com.fastbee.iot.service.IDeviceService;
|
import com.fastbee.iot.service.IDeviceService;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
@ -493,4 +494,15 @@ public class DeviceController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据子产品批量新增子设备
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('iot:device:add')")
|
||||||
|
@Log(title = "批量新增子设备", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping("/addBatchByProduct")
|
||||||
|
@ApiOperation("根据子产品批量新增子设备")
|
||||||
|
public AjaxResult addBatchByProduct(@RequestBody SubDeviceCreateByProductVO vo) {
|
||||||
|
int count = deviceService.addSubDevicesByProducts(vo);
|
||||||
|
return AjaxResult.success("成功新增 " + count + " 个子设备");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static com.fastbee.common.exception.ServiceExceptionUtil.exception;
|
import static com.fastbee.common.exception.ServiceExceptionUtil.exception;
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ public class BaiduILocationService implements ILocationService {
|
|||||||
|
|
||||||
log.info("结构化地址:{}", address);
|
log.info("结构化地址:{}", address);
|
||||||
String baseUrl = FastBeeConstant.MAP.BAIDU_GEOCODING;
|
String baseUrl = FastBeeConstant.MAP.BAIDU_GEOCODING;
|
||||||
address = StringUtils.isEmpty(address) ? "湖南省长沙市岳麓区" : address;
|
address = StringUtils.isEmpty(address) || Objects.equals("内网IP", address) ? "湖南省长沙市岳麓区" : address;
|
||||||
String encodedAddress;
|
String encodedAddress;
|
||||||
try {
|
try {
|
||||||
encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8.toString());
|
encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8.toString());
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.fastbee.iot.model.gateWay;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据子产品批量新增子设备VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SubDeviceCreateByProductVO {
|
||||||
|
@ApiModelProperty("网关设备id")
|
||||||
|
private Long gwDeviceId;
|
||||||
|
|
||||||
|
@ApiModelProperty("子产品设备创建项")
|
||||||
|
private List<SubProductDeviceItem> items;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class SubProductDeviceItem {
|
||||||
|
@ApiModelProperty("子产品id")
|
||||||
|
private Long subProductId;
|
||||||
|
|
||||||
|
@ApiModelProperty("子设备详情列表")
|
||||||
|
private List<DeviceDetail> devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class DeviceDetail {
|
||||||
|
@ApiModelProperty("自定义设备名称")
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
|
@ApiModelProperty("自定义从机地址")
|
||||||
|
private Integer slaveId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,4 +51,10 @@ public class SubDeviceListVO {
|
|||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
private Long subProductId;
|
private Long subProductId;
|
||||||
|
|
||||||
|
@ApiModelProperty("子产品名称")
|
||||||
|
private String subProductName;
|
||||||
|
|
||||||
|
@ApiModelProperty("在线状态")
|
||||||
|
private Integer status;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import com.fastbee.iot.model.*;
|
|||||||
import com.fastbee.iot.model.ThingsModels.ThingsModelShadow;
|
import com.fastbee.iot.model.ThingsModels.ThingsModelShadow;
|
||||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||||
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
||||||
|
import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO;
|
||||||
import org.quartz.SchedulerException;
|
import org.quartz.SchedulerException;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -146,6 +147,14 @@ public interface IDeviceService {
|
|||||||
*/
|
*/
|
||||||
public Device insertDevice(Device device);
|
public Device insertDevice(Device device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据子产品批量新增子设备并绑定到网关
|
||||||
|
*
|
||||||
|
* @param vo 包含网关设备id和子产品设备创建项列表
|
||||||
|
* @return 新增的子设备数量
|
||||||
|
*/
|
||||||
|
public int addSubDevicesByProducts(SubDeviceCreateByProductVO vo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备关联用户
|
* 设备关联用户
|
||||||
*
|
*
|
||||||
|
|||||||
@ -43,6 +43,7 @@ import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
|||||||
import com.fastbee.iot.model.ThingsModels.ValueItem;
|
import com.fastbee.iot.model.ThingsModels.ValueItem;
|
||||||
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
import com.fastbee.iot.model.dto.ThingsModelDTO;
|
||||||
import com.fastbee.iot.model.gateWay.ProductSubGatewayVO;
|
import com.fastbee.iot.model.gateWay.ProductSubGatewayVO;
|
||||||
|
import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO;
|
||||||
import com.fastbee.iot.model.gateWay.SubDeviceListVO;
|
import com.fastbee.iot.model.gateWay.SubDeviceListVO;
|
||||||
import com.fastbee.iot.service.*;
|
import com.fastbee.iot.service.*;
|
||||||
import com.fastbee.iot.tdengine.service.ILogService;
|
import com.fastbee.iot.tdengine.service.ILogService;
|
||||||
@ -1189,6 +1190,109 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据子产品批量新增子设备并绑定到网关(支持自定义每个设备的名称和从机地址)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int addSubDevicesByProducts(SubDeviceCreateByProductVO vo) {
|
||||||
|
Long gwDeviceId = vo.getGwDeviceId();
|
||||||
|
List<SubDeviceCreateByProductVO.SubProductDeviceItem> items = vo.getItems();
|
||||||
|
if (items == null || items.isEmpty()) {
|
||||||
|
throw new ServiceException("子产品创建项不能为空");
|
||||||
|
}
|
||||||
|
Device gwDevice = deviceMapper.selectDeviceByDeviceId(gwDeviceId);
|
||||||
|
if (gwDevice == null) {
|
||||||
|
throw new ServiceException("网关设备不存在");
|
||||||
|
}
|
||||||
|
Product product = productService.selectProductByProductId(gwDevice.getProductId());
|
||||||
|
if (product == null) {
|
||||||
|
throw new ServiceException("网关产品不存在");
|
||||||
|
}
|
||||||
|
List<Long> subProductIdList = items.stream()
|
||||||
|
.map(SubDeviceCreateByProductVO.SubProductDeviceItem::getSubProductId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Map<Long, List<SubDeviceCreateByProductVO.DeviceDetail>> deviceDetailMap = items.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
SubDeviceCreateByProductVO.SubProductDeviceItem::getSubProductId,
|
||||||
|
i -> i.getDevices() != null ? i.getDevices() : Collections.emptyList()));
|
||||||
|
ProductSubGateway query = new ProductSubGateway();
|
||||||
|
query.setGwProductId(product.getProductId());
|
||||||
|
List<ProductSubGatewayVO> allSubProducts = productSubGatewayMapper.selectListVO(query);
|
||||||
|
List<ProductSubGatewayVO> selectedSubProducts = allSubProducts.stream()
|
||||||
|
.filter(p -> subProductIdList.contains(p.getSubProductId()))
|
||||||
|
.sorted(Comparator.comparing(ProductSubGatewayVO::getSlaveId))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isEmpty(selectedSubProducts)) {
|
||||||
|
throw new ServiceException("未找到匹配的子产品");
|
||||||
|
}
|
||||||
|
List<Long> productIds = selectedSubProducts.stream()
|
||||||
|
.map(ProductSubGatewayVO::getSubProductId).collect(Collectors.toList());
|
||||||
|
LambdaQueryWrapper<ProductModbusJob> jobQuery = new LambdaQueryWrapper<>();
|
||||||
|
jobQuery.in(ProductModbusJob::getProductId, productIds);
|
||||||
|
List<ProductModbusJob> productModbusJobList = productModbusJobMapper.selectList(jobQuery);
|
||||||
|
Map<Long, List<ProductModbusJob>> productModbusJobMap = productModbusJobList.stream()
|
||||||
|
.collect(Collectors.groupingBy(ProductModbusJob::getProductId));
|
||||||
|
int totalCount = 0;
|
||||||
|
for (ProductSubGatewayVO subProduct : selectedSubProducts) {
|
||||||
|
List<SubDeviceCreateByProductVO.DeviceDetail> details =
|
||||||
|
deviceDetailMap.getOrDefault(subProduct.getSubProductId(), Collections.emptyList());
|
||||||
|
if (details.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < details.size(); i++) {
|
||||||
|
SubDeviceCreateByProductVO.DeviceDetail detail = details.get(i);
|
||||||
|
String deviceName = StringUtils.isNotEmpty(detail.getDeviceName())
|
||||||
|
? detail.getDeviceName()
|
||||||
|
: subProduct.getSubProductName() + "_" + subProduct.getSlaveId() + "_" + (i + 1);
|
||||||
|
Integer slaveId = detail.getSlaveId() != null
|
||||||
|
? detail.getSlaveId()
|
||||||
|
: subProduct.getSlaveId();
|
||||||
|
Device addDevice = new Device();
|
||||||
|
addDevice.setDeviceName(deviceName);
|
||||||
|
addDevice.setProductId(subProduct.getSubProductId());
|
||||||
|
addDevice.setProductName(subProduct.getSubProductName());
|
||||||
|
addDevice.setSerialNumber(this.generationDeviceNum(1));
|
||||||
|
addDevice.setTenantId(gwDevice.getTenantId());
|
||||||
|
addDevice.setTenantName(gwDevice.getTenantName());
|
||||||
|
addDevice.setCreateBy(SecurityUtils.getUsername());
|
||||||
|
addDevice.setFirmwareVersion(new BigDecimal(1));
|
||||||
|
addDevice.setRssi(0);
|
||||||
|
addDevice.setIsShadow(0);
|
||||||
|
addDevice.setLocationWay(subProduct.getSubLocationWay());
|
||||||
|
addDevice.setImgUrl(product.getImgUrl());
|
||||||
|
addDevice.setCreateTime(new Date());
|
||||||
|
deviceMapper.insertDevice(addDevice);
|
||||||
|
SubGateway subGateway = new SubGateway();
|
||||||
|
subGateway.setGwDeviceId(gwDeviceId);
|
||||||
|
subGateway.setSubDeviceId(addDevice.getDeviceId());
|
||||||
|
subGateway.setSlaveId(slaveId);
|
||||||
|
subGateway.setCreateBy(SecurityUtils.getUsername());
|
||||||
|
subGateway.setCreateTime(new Date());
|
||||||
|
subGatewayMapper.insertGateway(subGateway);
|
||||||
|
List<ProductModbusJob> modbusJobs = productModbusJobMap.get(subProduct.getSubProductId());
|
||||||
|
if (!CollectionUtils.isEmpty(modbusJobs)) {
|
||||||
|
for (ProductModbusJob pmj : modbusJobs) {
|
||||||
|
ModbusJob modbusJob = new ModbusJob();
|
||||||
|
modbusJob.setJobName(pmj.getJobName());
|
||||||
|
modbusJob.setSubDeviceId(addDevice.getDeviceId());
|
||||||
|
modbusJob.setSubSerialNumber(addDevice.getSerialNumber());
|
||||||
|
modbusJob.setCommand(pmj.getCommand());
|
||||||
|
modbusJob.setStatus(pmj.getStatus());
|
||||||
|
modbusJob.setRemark(pmj.getRemark());
|
||||||
|
modbusJobService.insertModbusJob(modbusJob);
|
||||||
|
if (FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) {
|
||||||
|
redisCache.getCacheModbusTcpId(gwDevice.getSerialNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itslValueCache.addCacheDeviceStatus(addDevice.getProductId(), addDevice.getSerialNumber());
|
||||||
|
totalCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端-设备关联用户
|
* 终端-设备关联用户
|
||||||
*
|
*
|
||||||
|
|||||||
@ -30,6 +30,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="subDeviceNo" column="serial_number"/>
|
<result property="subDeviceNo" column="serial_number"/>
|
||||||
<result property="createTime" column="create_time" />
|
<result property="createTime" column="create_time" />
|
||||||
<result property="subProductId" column="sub_product_id"/>
|
<result property="subProductId" column="sub_product_id"/>
|
||||||
|
<result property="subProductName" column="sub_product_name"/>
|
||||||
|
<result property="status" column="status"/>
|
||||||
<result property="id" column="id" />
|
<result property="id" column="id" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
@ -47,8 +49,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectGatewayList" parameterType="com.fastbee.iot.domain.SubGateway" resultMap="GatewaySubDeviceListResult">
|
<select id="selectGatewayList" parameterType="com.fastbee.iot.domain.SubGateway" resultMap="GatewaySubDeviceListResult">
|
||||||
select ga.id, ga.gw_device_id, ga.sub_device_id, ga.slave_id, ga.create_time ,d.device_name, d.serial_number, d.product_id sub_product_id
|
select ga.id, ga.gw_device_id, ga.sub_device_id, ga.slave_id, ga.create_time,
|
||||||
from iot_sub_gateway ga inner join iot_device d on ga.sub_device_id = d.device_id
|
d.device_name, d.serial_number, d.product_id sub_product_id, d.status,
|
||||||
|
p.product_name as sub_product_name
|
||||||
|
from iot_sub_gateway ga
|
||||||
|
inner join iot_device d on ga.sub_device_id = d.device_id
|
||||||
|
left join iot_product p on d.product_id = p.product_id
|
||||||
<where>
|
<where>
|
||||||
<if test="gwDeviceId != null and gwDeviceId != 0">
|
<if test="gwDeviceId != null and gwDeviceId != 0">
|
||||||
gw_device_id = #{gwDeviceId}
|
gw_device_id = #{gwDeviceId}
|
||||||
@ -57,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
sub_device_id = #{subDeviceId}
|
sub_device_id = #{subDeviceId}
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
|
order by p.product_name, ga.slave_id
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectGatewayById" parameterType="Long" resultMap="GatewayResult">
|
<select id="selectGatewayById" parameterType="Long" resultMap="GatewayResult">
|
||||||
|
|||||||
@ -70,6 +70,15 @@ export function editGatewayBatch(data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据子产品批量新增子设备
|
||||||
|
export function createSubDeviceByProducts(data) {
|
||||||
|
return request({
|
||||||
|
url: '/iot/device/addBatchByProduct',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
///产品///
|
///产品///
|
||||||
// 查询网关与子产品关联列表
|
// 查询网关与子产品关联列表
|
||||||
export function gatewayProductList(query) {
|
export function gatewayProductList(query) {
|
||||||
|
|||||||
@ -571,12 +571,30 @@
|
|||||||
"device.running-status.866086-29": "The number of elements does not match, the number of array elements is",
|
"device.running-status.866086-29": "The number of elements does not match, the number of array elements is",
|
||||||
"device.running-status.866086-30": "Number, separated by English commas.",
|
"device.running-status.866086-30": "Number, separated by English commas.",
|
||||||
"device.running-status.866086-31": "Equipment upgrade",
|
"device.running-status.866086-31": "Equipment upgrade",
|
||||||
"device.sub.083943-0": "Add sub devices",
|
"device.sub.binding": "Binding sub devices",
|
||||||
|
"device.sub.createByProduct": "Add Sub Devices",
|
||||||
|
"device.sub-device-create.title": "Add Sub Devices by Products",
|
||||||
|
"device.sub-device-create.productListTitle": "Sub Product List",
|
||||||
|
"device.sub-device-create.noSubProducts": "No sub products found. Please configure sub products in product management first.",
|
||||||
|
"device.sub-device-create.enabled": "Enabled",
|
||||||
|
"device.sub-device-create.deviceConfig": "Device Config",
|
||||||
|
"device.sub-device-create.deviceCount": "Device Count",
|
||||||
|
"device.sub-device-create.deviceListTitle": "Device List",
|
||||||
|
"device.sub-device-create.summary": "Selected {0} sub products, will create {1} sub devices",
|
||||||
|
"device.sub-device-create.devicePrefix": "Sub Device",
|
||||||
|
"device.sub-device-create.deviceNameLabel": "Device Name",
|
||||||
|
"device.sub-device-create.deviceNamePlaceholder": "Please enter device name",
|
||||||
|
"device.sub-device-create.slaveIdLabel": "Slave Address",
|
||||||
|
"device.sub-device-create.slaveIdPlaceholder": "Please enter slave address",
|
||||||
|
"device.sub-device-create.selectTip": "Please enable at least one sub product and set device count",
|
||||||
|
"device.sub-device-create.success": "Sub devices added successfully",
|
||||||
"device.sub.083943-1": "Remove sub devices",
|
"device.sub.083943-1": "Remove sub devices",
|
||||||
"device.sub.083943-2": "Set sub device address",
|
"device.sub.083943-2": "Set sub device address",
|
||||||
"device.sub.083943-3": "Sub device address",
|
"device.sub.083943-3": "Sub device address",
|
||||||
"device.sub.083943-4": "Are you sure to delete the data item with device number {0]?",
|
"device.sub.083943-4": "Are you sure to delete the data item with device number {0]?",
|
||||||
"device.sub.083943-5": "Save successful",
|
"device.sub.083943-5": "Save successful",
|
||||||
|
"device.sub.subProductName": "Sub Product",
|
||||||
|
"device.sub.onlineStatus": "Online Status",
|
||||||
"device.sub-device-list.323213-0": "Select device",
|
"device.sub-device-list.323213-0": "Select device",
|
||||||
"device.sub-device-list.323213-1": "Please enter the sub device number",
|
"device.sub-device-list.323213-1": "Please enter the sub device number",
|
||||||
"device.sub-device-list.323213-2": "name",
|
"device.sub-device-list.323213-2": "name",
|
||||||
|
|||||||
@ -571,12 +571,30 @@
|
|||||||
"device.running-status.866086-29": "元素个数不匹配,数组元素个数为",
|
"device.running-status.866086-29": "元素个数不匹配,数组元素个数为",
|
||||||
"device.running-status.866086-30": "个,以英文逗号分隔。",
|
"device.running-status.866086-30": "个,以英文逗号分隔。",
|
||||||
"device.running-status.866086-31": "设备升级",
|
"device.running-status.866086-31": "设备升级",
|
||||||
"device.sub.083943-0": "添加子设备",
|
"device.sub.binding": "绑定子设备",
|
||||||
|
"device.sub.createByProduct": "新增子设备",
|
||||||
|
"device.sub-device-create.title": "根据子产品新增子设备",
|
||||||
|
"device.sub-device-create.productListTitle": "子产品列表",
|
||||||
|
"device.sub-device-create.noSubProducts": "当前网关产品下暂无子产品,请先在产品管理中配置子产品",
|
||||||
|
"device.sub-device-create.enabled": "已启用",
|
||||||
|
"device.sub-device-create.deviceConfig": "子设备配置",
|
||||||
|
"device.sub-device-create.deviceCount": "子设备数量",
|
||||||
|
"device.sub-device-create.deviceListTitle": "子设备列表",
|
||||||
|
"device.sub-device-create.summary": "已选择 {0} 个子产品,将新增 {1} 个子设备",
|
||||||
|
"device.sub-device-create.devicePrefix": "子设备",
|
||||||
|
"device.sub-device-create.deviceNameLabel": "子设备名称",
|
||||||
|
"device.sub-device-create.deviceNamePlaceholder": "请输入子设备名称",
|
||||||
|
"device.sub-device-create.slaveIdLabel": "从机地址",
|
||||||
|
"device.sub-device-create.slaveIdPlaceholder": "请输入从机地址",
|
||||||
|
"device.sub-device-create.selectTip": "请至少启用一个子产品并设置设备数量",
|
||||||
|
"device.sub-device-create.success": "新增子设备成功",
|
||||||
"device.sub.083943-1": "移除子设备",
|
"device.sub.083943-1": "移除子设备",
|
||||||
"device.sub.083943-2": "设置子设备地址",
|
"device.sub.083943-2": "设置子设备地址",
|
||||||
"device.sub.083943-3": "子设备地址",
|
"device.sub.083943-3": "子设备地址",
|
||||||
"device.sub.083943-4": "是否确认删除设备编号为{0]的数据项?",
|
"device.sub.083943-4": "是否确认删除设备编号为{0]的数据项?",
|
||||||
"device.sub.083943-5": "保存成功",
|
"device.sub.083943-5": "保存成功",
|
||||||
|
"device.sub.subProductName": "所属子产品",
|
||||||
|
"device.sub.onlineStatus": "在线状态",
|
||||||
"device.sub-device-list.323213-0": "选择设备",
|
"device.sub-device-list.323213-0": "选择设备",
|
||||||
"device.sub-device-list.323213-1": "请输入子设备编号",
|
"device.sub-device-list.323213-1": "请输入子设备编号",
|
||||||
"device.sub-device-list.323213-2": "名称",
|
"device.sub-device-list.323213-2": "名称",
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
<div class="device-sub-container">
|
<div class="device-sub-container">
|
||||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button plain type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">{{ $t('device.sub.083943-0') }}</el-button>
|
<el-button plain type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">{{ $t('device.sub.binding') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button plain type="success" icon="el-icon-plus" size="mini" @click="handleCreateByProduct">{{ $t('device.sub.createByProduct') }}</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button plain type="danger" icon="el-icon-delete" size="mini" @click="handleDelete" :disabled="this.ids.length == 0">{{ $t('device.sub.083943-1') }}</el-button>
|
<el-button plain type="danger" icon="el-icon-delete" size="mini" @click="handleDelete" :disabled="this.ids.length == 0">{{ $t('device.sub.083943-1') }}</el-button>
|
||||||
@ -22,8 +25,13 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange" :border="false">
|
<el-table v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange" :border="true" :span-method="mergeProductRows">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column :label="$t('device.sub.subProductName')" align="center" prop="subProductName" min-width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag size="small" effect="plain">{{ scope.row.subProductName || '-' }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$t('device.device-edit.148398-1')" align="center" prop="subDeviceName" />
|
<el-table-column :label="$t('device.device-edit.148398-1')" align="center" prop="subDeviceName" />
|
||||||
<el-table-column :label="$t('device.device-edit.148398-7')" align="center" prop="subDeviceNo" />
|
<el-table-column :label="$t('device.device-edit.148398-7')" align="center" prop="subDeviceNo" />
|
||||||
<el-table-column :label="$t('device.sub.083943-3')" align="center" prop="slaveId" width="180px">
|
<el-table-column :label="$t('device.sub.083943-3')" align="center" prop="slaveId" width="180px">
|
||||||
@ -31,22 +39,31 @@
|
|||||||
<el-input style="width: 120px; text-align: center" :disabled="!isSet" v-model="scope.row.slaveId" size="small" :placeholder="$t('device.sub.083943-2')"></el-input>
|
<el-input style="width: 120px; text-align: center" :disabled="!isSet" v-model="scope.row.slaveId" size="small" :placeholder="$t('device.sub.083943-2')"></el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column label="在线状态" align="center" prop="status">-->
|
<el-table-column :label="$t('device.sub.onlineStatus')" align="center" prop="status">
|
||||||
<!-- <template slot-scope="scope">-->
|
<template slot-scope="scope">
|
||||||
<!-- <dict-tag :options="dict.type.iot_device_status" :value="scope.row.status" />-->
|
<dict-tag :options="dict.type.iot_device_status" :value="scope.row.status" />
|
||||||
<!-- </template>-->
|
</template>
|
||||||
<!-- </el-table-column>-->
|
</el-table-column>
|
||||||
<el-table-column :label="$t('creatTime')" align="center" prop="createTime" width="180">
|
<el-table-column :label="$t('creatTime')" align="center" prop="createTime" width="180">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('opation')" align="center" class-name="small-padding fixed-width" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="goDeviceDetail(scope.row.subDeviceId)">{{ $t('detail') }}</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">{{ $t('device.sub.083943-1') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||||
|
|
||||||
<!-- 子设备列表 -->
|
<!-- 子设备列表 -->
|
||||||
<subDeviceList ref="subDeviceList" :gateway="gateway" @addSuccess="addSuccess"></subDeviceList>
|
<subDeviceList ref="subDeviceList" :gateway="gateway" @addSuccess="addSuccess"></subDeviceList>
|
||||||
|
|
||||||
|
<!-- 根据子产品新增子设备 -->
|
||||||
|
<subDeviceCreate ref="subDeviceCreate" :gateway="gateway" @addSuccess="addSuccess"></subDeviceCreate>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -54,6 +71,7 @@
|
|||||||
import { listGateway, delGateway, editGatewayBatch } from '@/api/iot/gateway';
|
import { listGateway, delGateway, editGatewayBatch } from '@/api/iot/gateway';
|
||||||
|
|
||||||
import subDeviceList from './sub-device-list';
|
import subDeviceList from './sub-device-list';
|
||||||
|
import subDeviceCreate from './sub-device-create';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'device-sub',
|
name: 'device-sub',
|
||||||
@ -65,6 +83,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
subDeviceList,
|
subDeviceList,
|
||||||
|
subDeviceCreate,
|
||||||
},
|
},
|
||||||
dicts: ['iot_device_status'],
|
dicts: ['iot_device_status'],
|
||||||
data() {
|
data() {
|
||||||
@ -98,6 +117,8 @@ export default {
|
|||||||
gateway: {},
|
gateway: {},
|
||||||
//是否可设置子设备地址
|
//是否可设置子设备地址
|
||||||
isSet: false,
|
isSet: false,
|
||||||
|
// 子产品行合并信息
|
||||||
|
productSpans: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -117,13 +138,39 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
/** 查询子设备列表 */
|
/** 查询子设备列表 */
|
||||||
getList() {
|
getList() {
|
||||||
// this.loading = true;
|
|
||||||
listGateway(this.queryParams).then((response) => {
|
listGateway(this.queryParams).then((response) => {
|
||||||
this.deviceList = response.rows;
|
this.deviceList = response.rows;
|
||||||
this.total = response.total;
|
this.total = response.total;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
this.computeProductSpans();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
computeProductSpans() {
|
||||||
|
const spans = [];
|
||||||
|
const list = this.deviceList || [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < list.length) {
|
||||||
|
const productId = list[i].subProductId;
|
||||||
|
let count = 1;
|
||||||
|
while (i + count < list.length && list[i + count].subProductId === productId) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
for (let j = 0; j < count; j++) {
|
||||||
|
spans.push(j === 0 ? count : 0);
|
||||||
|
}
|
||||||
|
i += count;
|
||||||
|
}
|
||||||
|
this.productSpans = spans;
|
||||||
|
},
|
||||||
|
mergeProductRows({ row, column, rowIndex, columnIndex }) {
|
||||||
|
if (columnIndex === 1) {
|
||||||
|
const span = this.productSpans[rowIndex];
|
||||||
|
if (span > 0) {
|
||||||
|
return { rowspan: span, colspan: 1 };
|
||||||
|
}
|
||||||
|
return { rowspan: 0, colspan: 0 };
|
||||||
|
}
|
||||||
|
},
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
cancel() {
|
cancel() {
|
||||||
this.open = false;
|
this.open = false;
|
||||||
@ -162,6 +209,14 @@ export default {
|
|||||||
this.$refs.subDeviceList.openDeviceList = true;
|
this.$refs.subDeviceList.openDeviceList = true;
|
||||||
this.$refs.subDeviceList.getList();
|
this.$refs.subDeviceList.getList();
|
||||||
},
|
},
|
||||||
|
/** 根据子产品新增子设备 */
|
||||||
|
handleCreateByProduct() {
|
||||||
|
if (!this.device || !this.device.productId) {
|
||||||
|
this.$modal.msgWarning(this.$t('device.device-edit.148398-72'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.subDeviceCreate.open(this.device.productId);
|
||||||
|
},
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const deviceIds = row.deviceId || this.ids;
|
const deviceIds = row.deviceId || this.ids;
|
||||||
@ -195,6 +250,15 @@ export default {
|
|||||||
addSuccess() {
|
addSuccess() {
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
|
/** 跳转子设备详情页 */
|
||||||
|
goDeviceDetail(deviceId) {
|
||||||
|
if (!deviceId) return;
|
||||||
|
const route = this.$router.resolve({
|
||||||
|
path: '/iot/device-edit',
|
||||||
|
query: { deviceId: deviceId, isSubDev: 0, pageNum: 1, activeName: 'basic' },
|
||||||
|
});
|
||||||
|
window.open(route.href, '_blank');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
355
yack-web/src/views/iot/device/sub-device-create.vue
Normal file
355
yack-web/src/views/iot/device/sub-device-create.vue
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="$t('device.sub-device-create.title')"
|
||||||
|
:visible.sync="visible"
|
||||||
|
width="720px"
|
||||||
|
append-to-body
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div v-loading="loading" class="sub-device-create">
|
||||||
|
<div class="section-title">
|
||||||
|
<i class="el-icon-s-grid" style="margin-right: 4px"></i>
|
||||||
|
{{ $t('device.sub-device-create.productListTitle') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="subProductList.length === 0 && !loading" class="empty-tip">
|
||||||
|
{{ $t('device.sub-device-create.noSubProducts') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item
|
||||||
|
v-for="(item, index) in subProductList"
|
||||||
|
:key="item.subProductId"
|
||||||
|
:name="index"
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<div class="product-header">
|
||||||
|
<i class="el-icon-cpu" style="color: #409eff; margin-right: 6px"></i>
|
||||||
|
<span class="product-name">{{ item.subProductName }}</span>
|
||||||
|
<el-tag v-if="item.enabled" size="mini" type="success" effect="plain" style="margin-left: 8px">
|
||||||
|
{{ $t('device.sub-device-create.enabled') }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="item.enabled" size="mini" effect="plain" style="margin-left: 4px">
|
||||||
|
x{{ item.devices.length }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="product-body">
|
||||||
|
<el-row :gutter="24" class="config-row">
|
||||||
|
<el-col :span="10">
|
||||||
|
<div class="config-item">
|
||||||
|
<span class="config-label">{{ $t('device.sub-device-create.deviceConfig') }}</span>
|
||||||
|
<el-switch v-model="item.enabled" active-color="#409eff" @change="onEnableChange(index, $event)" />
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="14">
|
||||||
|
<div class="config-item">
|
||||||
|
<span class="config-label">{{ $t('device.sub-device-create.deviceCount') }}</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="item.count"
|
||||||
|
:min="1"
|
||||||
|
:max="10"
|
||||||
|
:disabled="!item.enabled"
|
||||||
|
size="small"
|
||||||
|
@change="onCountChange(index)"
|
||||||
|
@input.native="onCountInput(index, $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div v-if="item.enabled && item.devices.length > 0" class="device-list-section">
|
||||||
|
<div class="device-list-title">
|
||||||
|
<i class="el-icon-menu" style="margin-right: 4px; color: #409eff"></i>
|
||||||
|
{{ $t('device.sub-device-create.deviceListTitle') }}
|
||||||
|
</div>
|
||||||
|
<el-collapse v-model="item.expandedDevices" class="device-collapse">
|
||||||
|
<el-collapse-item
|
||||||
|
v-for="(dev, dIdx) in item.devices"
|
||||||
|
:key="dIdx"
|
||||||
|
:name="dIdx"
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<div class="device-header">
|
||||||
|
<i class="el-icon-monitor" style="color: #909399; margin-right: 6px"></i>
|
||||||
|
<span>{{ dev.deviceName || ($t('device.sub-device-create.devicePrefix') + (dIdx + 1)) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="device-form">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="14">
|
||||||
|
<div class="device-form-item">
|
||||||
|
<span class="device-form-label">{{ $t('device.sub-device-create.deviceNameLabel') }}</span>
|
||||||
|
<el-input
|
||||||
|
v-model="dev.deviceName"
|
||||||
|
size="small"
|
||||||
|
:placeholder="$t('device.sub-device-create.deviceNamePlaceholder')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<div class="device-form-item">
|
||||||
|
<span class="device-form-label">{{ $t('device.sub-device-create.slaveIdLabel') }}</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="dev.slaveId"
|
||||||
|
:min="1"
|
||||||
|
:max="255"
|
||||||
|
size="small"
|
||||||
|
:placeholder="$t('device.sub-device-create.slaveIdPlaceholder')"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
|
||||||
|
<div v-if="totalDeviceCount > 0" class="summary-bar">
|
||||||
|
<i class="el-icon-info" style="color: #409eff; margin-right: 4px"></i>
|
||||||
|
{{ $t('device.sub-device-create.summary', [enabledProductCount, totalDeviceCount]) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" :loading="submitting" :disabled="totalDeviceCount === 0" @click="handleConfirm">
|
||||||
|
{{ $t('confirm') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleClose">{{ $t('cancel') }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gatewayProductList, createSubDeviceByProducts } from '@/api/iot/gateway';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'sub-device-create',
|
||||||
|
props: {
|
||||||
|
gateway: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
subProductList: [],
|
||||||
|
activeNames: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
enabledProducts() {
|
||||||
|
return this.subProductList.filter((p) => p.enabled);
|
||||||
|
},
|
||||||
|
enabledProductCount() {
|
||||||
|
return this.enabledProducts.length;
|
||||||
|
},
|
||||||
|
totalDeviceCount() {
|
||||||
|
return this.enabledProducts.reduce((sum, p) => sum + (p.devices ? p.devices.length : 0), 0);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(productId) {
|
||||||
|
this.visible = true;
|
||||||
|
this.subProductList = [];
|
||||||
|
this.activeNames = [];
|
||||||
|
this.getSubProducts(productId);
|
||||||
|
},
|
||||||
|
getSubProducts(productId) {
|
||||||
|
this.loading = true;
|
||||||
|
gatewayProductList({ gwProductId: productId, pageNum: 1, pageSize: 100 })
|
||||||
|
.then((response) => {
|
||||||
|
const rows = response.rows || [];
|
||||||
|
this.subProductList = rows.map((item) => ({
|
||||||
|
...item,
|
||||||
|
enabled: false,
|
||||||
|
count: 1,
|
||||||
|
devices: [],
|
||||||
|
expandedDevices: [],
|
||||||
|
}));
|
||||||
|
this.activeNames = rows.map((_, idx) => idx);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onEnableChange(index, val) {
|
||||||
|
const item = this.subProductList[index];
|
||||||
|
if (val) {
|
||||||
|
this.rebuildDevices(index);
|
||||||
|
} else {
|
||||||
|
this.$set(item, 'devices', []);
|
||||||
|
this.$set(item, 'expandedDevices', []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCountChange(index) {
|
||||||
|
this.rebuildDevices(index);
|
||||||
|
},
|
||||||
|
onCountInput(index, e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.rebuildDevices(index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rebuildDevices(index) {
|
||||||
|
const item = this.subProductList[index];
|
||||||
|
if (!item.enabled) return;
|
||||||
|
const count = Math.max(1, Math.min(10, item.count || 1));
|
||||||
|
const baseSlaveId = item.slaveId || 1;
|
||||||
|
const oldDevices = item.devices || [];
|
||||||
|
const devices = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
if (i < oldDevices.length) {
|
||||||
|
devices.push(oldDevices[i]);
|
||||||
|
} else {
|
||||||
|
devices.push({
|
||||||
|
deviceName: item.subProductName + '_' + (i + 1),
|
||||||
|
slaveId: baseSlaveId + i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$set(item, 'devices', devices);
|
||||||
|
this.$set(item, 'expandedDevices', devices.map((_, i) => i));
|
||||||
|
},
|
||||||
|
handleConfirm() {
|
||||||
|
if (this.totalDeviceCount === 0) {
|
||||||
|
this.$modal.msgWarning(this.$t('device.sub-device-create.selectTip'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const items = this.enabledProducts.map((p) => ({
|
||||||
|
subProductId: p.subProductId,
|
||||||
|
devices: (p.devices || []).map((d) => ({
|
||||||
|
deviceName: d.deviceName,
|
||||||
|
slaveId: d.slaveId,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
this.submitting = true;
|
||||||
|
createSubDeviceByProducts({
|
||||||
|
gwDeviceId: this.gateway.gwDeviceId,
|
||||||
|
items: items,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
this.$modal.msgSuccess(response.msg || this.$t('device.sub-device-create.success'));
|
||||||
|
this.visible = false;
|
||||||
|
this.$emit('addSuccess');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.submitting = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sub-device-create {
|
||||||
|
max-height: 520px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.section-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
padding: 40px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.product-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.product-name {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.product-body {
|
||||||
|
padding: 8px 0 4px;
|
||||||
|
}
|
||||||
|
.config-row {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.config-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.config-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.device-list-section {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.device-list-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.device-collapse {
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.device-collapse >>> .el-collapse-item__header {
|
||||||
|
padding-left: 14px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 13px;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
.device-collapse >>> .el-collapse-item__wrap {
|
||||||
|
border-bottom: 1px solid #f2f6fc;
|
||||||
|
}
|
||||||
|
.device-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.device-form {
|
||||||
|
padding: 12px 14px 8px;
|
||||||
|
}
|
||||||
|
.device-form-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.device-form-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 56px;
|
||||||
|
}
|
||||||
|
.summary-bar {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: #ecf5ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #409eff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user