设备参数组绑定
diff --git a/build.gradle b/build.gradle
index cbfe262..566e7a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -55,6 +55,7 @@
     implementation group: 'javax.servlet', name: 'jstl', version: '1.2'
     implementation group: 'taglibs', name: 'standard', version: '1.1.2'
     implementation group: 'commons-codec', name: 'commons-codec', version: '1.6'
+    implementation 'org.apache.commons:commons-lang3:3.9'
 //    implementation 'org.flywaydb:flyway-core'
 //    implementation group: 'javax.servlet.jsp', name: 'jsp-api', version: '2.1'
     implementation group: 'log4j', name: 'log4j', version: '1.2.16'
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBindBean.java b/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBindBean.java
new file mode 100644
index 0000000..7066e1e
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBindBean.java
@@ -0,0 +1,70 @@
+package com.supwisdom.dlpay.restaurant.bean;
+
+public class DevparaBindBean {
+  private Integer groupid;
+  private String groupname;
+  private Integer deviceid;
+  private String devicename;
+  private String devphyid;
+  private String lastsaved;
+
+  public DevparaBindBean() {
+  }
+
+  public DevparaBindBean(Integer groupid, String groupname, Integer deviceid, String devicename, String devphyid, String lastsaved) {
+    this.groupid = groupid;
+    this.groupname = groupname;
+    this.deviceid = deviceid;
+    this.devicename = devicename;
+    this.devphyid = devphyid;
+    this.lastsaved = lastsaved;
+  }
+
+  public Integer getGroupid() {
+    return groupid;
+  }
+
+  public void setGroupid(Integer groupid) {
+    this.groupid = groupid;
+  }
+
+  public String getGroupname() {
+    return groupname;
+  }
+
+  public void setGroupname(String groupname) {
+    this.groupname = groupname;
+  }
+
+  public Integer getDeviceid() {
+    return deviceid;
+  }
+
+  public void setDeviceid(Integer deviceid) {
+    this.deviceid = deviceid;
+  }
+
+  public String getDevicename() {
+    return devicename;
+  }
+
+  public void setDevicename(String devicename) {
+    this.devicename = devicename;
+  }
+
+  public String getDevphyid() {
+    return devphyid;
+  }
+
+  public void setDevphyid(String devphyid) {
+    this.devphyid = devphyid;
+  }
+
+  public String getLastsaved() {
+    return lastsaved;
+  }
+
+  public void setLastsaved(String lastsaved) {
+    this.lastsaved = lastsaved;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBinddevShowBean.java b/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBinddevShowBean.java
new file mode 100644
index 0000000..6cdfbd1
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/bean/DevparaBinddevShowBean.java
@@ -0,0 +1,60 @@
+package com.supwisdom.dlpay.restaurant.bean;
+
+public class DevparaBinddevShowBean {
+  private Integer deviceid;
+  private String devicename;
+  private String devphyid;
+  private Integer devgroupid;
+  private String devgroupname;
+
+  public DevparaBinddevShowBean() {
+  }
+
+  public DevparaBinddevShowBean(Integer deviceid, String devicename, String devphyid, Integer devgroupid, String devgroupname) {
+    this.deviceid = deviceid;
+    this.devicename = devicename;
+    this.devphyid = devphyid;
+    this.devgroupid = devgroupid;
+    this.devgroupname = devgroupname;
+  }
+
+  public Integer getDeviceid() {
+    return deviceid;
+  }
+
+  public void setDeviceid(Integer deviceid) {
+    this.deviceid = deviceid;
+  }
+
+  public String getDevicename() {
+    return devicename;
+  }
+
+  public void setDevicename(String devicename) {
+    this.devicename = devicename;
+  }
+
+  public String getDevphyid() {
+    return devphyid;
+  }
+
+  public void setDevphyid(String devphyid) {
+    this.devphyid = devphyid;
+  }
+
+  public Integer getDevgroupid() {
+    return devgroupid;
+  }
+
+  public void setDevgroupid(Integer devgroupid) {
+    this.devgroupid = devgroupid;
+  }
+
+  public String getDevgroupname() {
+    return devgroupname;
+  }
+
+  public void setDevgroupname(String devgroupname) {
+    this.devgroupname = devgroupname;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java b/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java
index 7ffc9b4..afaacae 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/controller/DeviceParamController.java
@@ -6,14 +6,19 @@
 import com.supwisdom.dlpay.framework.util.PageResult;
 import com.supwisdom.dlpay.framework.util.StringUtil;
 import com.supwisdom.dlpay.framework.util.WebConstant;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBindBean;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBinddevShowBean;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaBind;
 import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
 import com.supwisdom.dlpay.restaurant.service.DeviceParamService;
+import com.supwisdom.dlpay.system.bean.TreeSelectNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.List;
 import java.util.Map;
 
 @Controller
@@ -102,7 +107,107 @@
       if (deviceParamService.saveOrUpdateDevpara(groupid, groupname, param)) {
         return JsonResult.ok(groupid == 0 ? "新增成功" : "修改成功");
       } else {
-        return JsonResult.ok(groupid == 0 ? "新增失败" : "修改失败");
+        return JsonResult.error(groupid == 0 ? "新增失败" : "修改失败");
+      }
+    } catch (WebCheckException ex) {
+      return JsonResult.error(ex.getMessage());
+    } catch (Exception e) {
+      e.printStackTrace();
+      return JsonResult.error("系统处理异常").put("exception", e);
+    }
+  }
+
+  @GetMapping("/device/devparabind")
+  public String devparaBindView() {
+    return "restaurant/devpara/devparabind";
+  }
+
+  @GetMapping("/device/devparabindlist")
+  @PreAuthorize("hasPermission('/device/devparabind','')")
+  @ResponseBody
+  public PageResult<DevparaBindBean> getDevparaBindData(@RequestParam("page") Integer pageNo,
+                                                        @RequestParam("limit") Integer pageSize,
+                                                        @RequestParam(value = "searchkey", required = false) String searchkey) {
+    try {
+      if (null == pageNo || pageNo < 1) pageNo = WebConstant.PAGENO_DEFAULT;
+      if (null == pageSize || pageSize < 1) pageSize = WebConstant.PAGESIZE_DEFAULT;
+      return deviceParamService.getDevparaBindInfos(searchkey, pageNo, pageSize);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return new PageResult<>(99, "系统查询错误");
+    }
+  }
+
+  @PostMapping("/device/deletedevparabind")
+  @PreAuthorize("hasPermission('/device/deletedevparabind','')")
+  @ResponseBody
+  public JsonResult deleteDevparaBind(@RequestParam("deviceid") Integer deviceid) {
+    if (null == deviceid) {
+      return JsonResult.error("参数传递错误");
+    }
+    try {
+      TDevparaBind bind = deviceParamService.getDevparaBindByDeviceid(deviceid);
+      if (null == bind) {
+        return JsonResult.error("设备参数组绑定关系不存在!");
+      }
+
+      if (deviceParamService.deleteDevparaBind(bind)) {
+        return JsonResult.ok("删除成功");
+      } else {
+        return JsonResult.error("删除失败");
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      return JsonResult.error("系统处理异常").put("exception", e);
+    }
+  }
+
+  @GetMapping("/device/load4binddevpara")
+  @PreAuthorize("hasPermission('/device/load4binddevpara','')")
+  public String load4BindDevpara(Model model) {
+    model.addAttribute("grouplist", deviceParamService.getDevparaGroups());
+    return "restaurant/devpara/devparabindform";
+  }
+
+  @GetMapping("/device/load4binddevparalist")
+  @PreAuthorize("hasPermission('/device/load4binddevpara','')")
+  @ResponseBody
+  public PageResult<DevparaBinddevShowBean> searchBindDevparaDevices(@RequestParam(value = "devgroupid", required = false) Integer devgroupid,
+                                                                     @RequestParam(value = "searchkey", required = false) String searchkey) {
+    try {
+      if (null == devgroupid && StringUtil.isEmpty(searchkey)) {
+        return new PageResult<>(99, "请填写条件查询设备");
+      }
+
+      return deviceParamService.getBindDevicesBySearch(devgroupid, searchkey);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return new PageResult<>(99, "系统查询错误");
+    }
+  }
+
+  @GetMapping("/device/binddevparagrouptree")
+  @PreAuthorize("hasPermission('/device/load4binddevpara','')")
+  @ResponseBody
+  public List<TreeSelectNode> searchShopTree() {
+    List<TreeSelectNode> tree = deviceParamService.getDeviceGroupSelectTree();
+    return tree;
+  }
+
+  @PostMapping("/device/addbinddevpara")
+  @PreAuthorize("hasPermission('/device/addbinddevpara','')")
+  @ResponseBody
+  public JsonResult addDevparaBind(@RequestParam("groupid") Integer groupid,
+                                   @RequestParam("deviceid[]") List<Integer> deviceIds) {
+    try {
+      if(null==groupid || StringUtil.isEmpty(deviceIds)){
+        return JsonResult.error("参数传递错误");
+      }
+
+      if (deviceParamService.saveBindDevpara(groupid, deviceIds)) {
+        return JsonResult.ok("绑定成功");
+      } else {
+        return JsonResult.error("绑定失败");
       }
     } catch (WebCheckException ex) {
       return JsonResult.error(ex.getMessage());
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java
index 67e32a5..32e7dc8 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/DevparaBindDao.java
@@ -10,4 +10,6 @@
 
   @Query("select count(t.deviceid) from TDevparaBind t where t.groupid=?1")
   long getDevparaBindRecordcnt(int groupid);
+
+  TDevparaBind getByDeviceid(int deviceid);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java
index 9ea1508..8b6ba10 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/DeviceParamService.java
@@ -2,8 +2,13 @@
 
 import com.supwisdom.dlpay.exception.WebCheckException;
 import com.supwisdom.dlpay.framework.util.PageResult;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBindBean;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBinddevShowBean;
+import com.supwisdom.dlpay.restaurant.domain.TDevice;
 import com.supwisdom.dlpay.restaurant.domain.TDevpara;
+import com.supwisdom.dlpay.restaurant.domain.TDevparaBind;
 import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+import com.supwisdom.dlpay.system.bean.TreeSelectNode;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
@@ -25,6 +30,25 @@
   @Transactional(rollbackFor = Exception.class)
   boolean saveOrUpdateDevpara(int groupid, String groupname, Map<String, String> param) throws WebCheckException;
 
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  PageResult<DevparaBindBean> getDevparaBindInfos(String searchkey, int pageNo, int pageSize);
 
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  TDevparaBind getDevparaBindByDeviceid(Integer deviceid);
+
+  @Transactional(rollbackFor = Exception.class)
+  boolean deleteDevparaBind(TDevparaBind bind);
+
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  List<TDevparaGroup> getDevparaGroups();
+
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  PageResult<DevparaBinddevShowBean> getBindDevicesBySearch(Integer devgroupid, String searchkey);
+
+  @Transactional(rollbackFor = Exception.class, readOnly = true)
+  List<TreeSelectNode> getDeviceGroupSelectTree();
+
+  @Transactional(rollbackFor = Exception.class)
+  boolean saveBindDevpara(int groupid, List<Integer> deviceIds) throws WebCheckException;
 
 }
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java
index c59cad9..8dd96be 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/DeviceParamServiceImpl.java
@@ -4,18 +4,25 @@
 import com.supwisdom.dlpay.framework.service.SystemUtilService;
 import com.supwisdom.dlpay.framework.util.PageResult;
 import com.supwisdom.dlpay.framework.util.StringUtil;
-import com.supwisdom.dlpay.restaurant.dao.DevparaBindDao;
-import com.supwisdom.dlpay.restaurant.dao.DevparaDao;
-import com.supwisdom.dlpay.restaurant.dao.DevparaGroupDao;
-import com.supwisdom.dlpay.restaurant.domain.TDevpara;
-import com.supwisdom.dlpay.restaurant.domain.TDevparaGroup;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBindBean;
+import com.supwisdom.dlpay.restaurant.bean.DevparaBinddevShowBean;
+import com.supwisdom.dlpay.restaurant.dao.*;
+import com.supwisdom.dlpay.restaurant.domain.*;
 import com.supwisdom.dlpay.restaurant.service.DeviceParamService;
+import com.supwisdom.dlpay.system.bean.TreeSelectNode;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.query.internal.NativeQueryImpl;
+import org.hibernate.transform.Transformers;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Service;
 
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -29,8 +36,16 @@
   @Autowired
   private DevparaDao devparaDao;
   @Autowired
+  private DeviceDao deviceDao;
+  @Autowired
+  private DeviceGroupDao deviceGroupDao;
+
+  @Autowired
   private SystemUtilService systemUtilService;
 
+  @PersistenceContext
+  private EntityManager entityManager;
+
   @Override
   public PageResult<TDevparaGroup> getDevparaGroupPage(String groupname, int pageNo, int pageSize) {
     Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by("groupid"));
@@ -132,4 +147,140 @@
     }
     return true;
   }
+
+  @Override
+  public PageResult<DevparaBindBean> getDevparaBindInfos(String searchkey, int pageNo, int pageSize) {
+    StringBuffer querySql = new StringBuffer("select t.groupid,a.groupname,t.deviceid,b.devicename,b.devphyid,t.lastsaved " +
+        " from TB_DEVPARA_BIND t left join TB_DEVPARA_GROUP a on t.groupid=a.groupid " +
+        " left join TB_DEVICE b on t.deviceid=b.id where 1=1 ");
+    StringBuffer countSql = new StringBuffer("select count(t.deviceid) as cnt from TB_DEVPARA_BIND t left join TB_DEVPARA_GROUP a on t.groupid=a.groupid " +
+        " left join TB_DEVICE b on t.deviceid=b.id where 1=1 ");
+    if (!StringUtil.isEmpty(searchkey)) {
+      querySql.append(" and (a.groupname like :str or b.devicename like :str or b.devphyid like :str) ");
+      countSql.append(" and (a.groupname like :str or b.devicename like :str or b.devphyid like :str) ");
+    }
+    querySql.append(" order by t.deviceid ");
+    Query query = entityManager.createNativeQuery(querySql.toString());
+    Query countQuery = entityManager.createNativeQuery(countSql.toString());
+    if (!StringUtil.isEmpty(searchkey)) {
+      query.setParameter("str", "%" + searchkey.trim() + "%");
+      countQuery.setParameter("str", "%" + searchkey.trim() + "%");
+    }
+    query.setFirstResult((pageNo - 1) * pageSize);
+    query.setMaxResults(pageSize); //分页显示
+    query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(DevparaBindBean.class));
+    List<DevparaBindBean> list = query.getResultList();
+    BigInteger count = (BigInteger) countQuery.getSingleResult();
+    return new PageResult<>(count.longValue(), list);
+  }
+
+  @Override
+  public TDevparaBind getDevparaBindByDeviceid(Integer deviceid) {
+    if (null != deviceid) {
+      return devparaBindDao.getByDeviceid(deviceid);
+    }
+    return null;
+  }
+
+  @Override
+  public boolean deleteDevparaBind(TDevparaBind bind) {
+    if (null != bind) {
+      devparaBindDao.delete(bind);
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public List<TDevparaGroup> getDevparaGroups() {
+    List<TDevparaGroup> list = devparaGroupDao.findAll();
+    if (!StringUtil.isEmpty(list)) {
+      return list;
+    }
+    return new ArrayList<>(0);
+  }
+
+  @Override
+  public PageResult<DevparaBinddevShowBean> getBindDevicesBySearch(Integer devgroupid, String searchkey) {
+    List<Integer> chirdGroupids = null;
+    if (null != devgroupid) {
+      Query chirdGroupQuery = entityManager.createNativeQuery(" WITH  RECURSIVE  r  AS ( " +
+          " SELECT * FROM tb_devicegroup WHERE devgroupid = :gid " +
+          " union ALL " +
+          " SELECT a.* FROM tb_devicegroup a, r WHERE a.pid = r.devgroupid ) " +
+          " SELECT devgroupid FROM r ORDER BY devgroupid ");
+      chirdGroupQuery.setParameter("gid", devgroupid.intValue());
+      chirdGroupids = chirdGroupQuery.getResultList(); //递归查询所有的子节点
+    }
+
+    StringBuffer sql = new StringBuffer("select t.id as deviceid,t.devicename,t.devphyid,t.devgroupid,a.groupname as devgroupname " +
+        " from TB_DEVICE t left join TB_DEVICEGROUP a on t.devgroupid=a.devgroupid " +
+        " left join TB_DEVPARA_BIND b on t.id=b.deviceid where b.groupid is null and t.state=1 ");
+    if (!StringUtil.isEmpty(chirdGroupids)) {
+      sql.append(" and t.devgroupid in ("+StringUtils.join(chirdGroupids.toArray(),",")+") ");
+    }
+    if (!StringUtil.isEmpty(searchkey)) {
+      sql.append(" and (t.devicename like :str or t.devphyid like :str) ");
+    }
+    sql.append(" order by t.id ");
+    Query query = entityManager.createNativeQuery(sql.toString());
+    if (!StringUtil.isEmpty(searchkey)) {
+      query.setParameter("str", "%" + searchkey.trim() + "%");
+    }
+    query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(DevparaBinddevShowBean.class));
+    List<DevparaBinddevShowBean> list = query.getResultList();
+    return new PageResult<>(list);
+  }
+
+  @Override
+  public List<TreeSelectNode> getDeviceGroupSelectTree() {
+    List<TDeviceGroup> groupList = deviceGroupDao.findAll();
+    if (StringUtil.isEmpty(groupList)) return new ArrayList<>(0);
+    return getDevgroupTree(groupList, null);
+  }
+
+  private List<TreeSelectNode> getDevgroupTree(List<TDeviceGroup> groupList, Integer pid) {
+    List<TreeSelectNode> result = new ArrayList<>(0);
+    for (TDeviceGroup gp : groupList) {
+      if ((null == pid && gp.getPid() == null) || (null != pid && pid.equals(gp.getPid()))) {
+        TreeSelectNode node = new TreeSelectNode();
+        node.setId(String.valueOf(gp.getDevgroupid()));
+        node.setName(gp.getGroupname());
+        node.setOpen(true);
+        node.setChecked(false);
+        List<TreeSelectNode> children = getDevgroupTree(groupList, gp.getDevgroupid().intValue());
+        if (!StringUtil.isEmpty(children)) {
+          node.setChildren(children);
+        } else {
+          node.setChildren(null);
+        }
+        result.add(node);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public boolean saveBindDevpara(int groupid, List<Integer> deviceIds) throws WebCheckException {
+    TDevparaGroup group = devparaGroupDao.findByGroupid(groupid);
+    if (null == group) {
+      throw new WebCheckException("所选设备参数组不存在");
+    }
+    for (Integer id : deviceIds) {
+      TDevice device = deviceDao.findTDeviceById(id);
+      if (null == device) {
+        throw new WebCheckException("终端编号为[" + id + "]的设备不存在");
+      } else if (1 != device.getState()) {
+        throw new WebCheckException("终端编号为[" + id + "]的设备状态异常");
+      }
+
+      TDevparaBind bind = new TDevparaBind();
+      bind.setDeviceid(device.getId());
+      bind.setGroupid(group.getGroupid());
+      bind.setLastsaved(systemUtilService.getSysdatetime().getHostdatetime());
+      devparaBindDao.save(bind);
+    }
+    return true;
+  }
+
 }
diff --git a/src/main/java/com/supwisdom/dlpay/system/bean/TreeSelectNode.java b/src/main/java/com/supwisdom/dlpay/system/bean/TreeSelectNode.java
new file mode 100644
index 0000000..32eda08
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/system/bean/TreeSelectNode.java
@@ -0,0 +1,52 @@
+package com.supwisdom.dlpay.system.bean;
+
+import java.util.List;
+
+public class TreeSelectNode {
+  private String id;
+  private String name;
+  private boolean open;
+  private boolean checked;
+  private List<TreeSelectNode> children;
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public boolean isOpen() {
+    return open;
+  }
+
+  public void setOpen(boolean open) {
+    this.open = open;
+  }
+
+  public boolean isChecked() {
+    return checked;
+  }
+
+  public void setChecked(boolean checked) {
+    this.checked = checked;
+  }
+
+  public List<TreeSelectNode> getChildren() {
+    return children;
+  }
+
+  public void setChildren(List<TreeSelectNode> children) {
+    this.children = children;
+  }
+
+}
diff --git a/src/main/resources/static/custom/css/admin.css b/src/main/resources/static/custom/css/admin.css
index 29547ad..7bfcc04 100755
--- a/src/main/resources/static/custom/css/admin.css
+++ b/src/main/resources/static/custom/css/admin.css
@@ -435,7 +435,7 @@
 .layui-form.toolbar .layui-form-select input {
     height: 35px;
     line-height: 35px;
-    width: 150px;
+    /*width: 150px;*/
     overflow: hidden;
 }
 
diff --git a/src/main/resources/templates/restaurant/devpara/devparabind.html b/src/main/resources/templates/restaurant/devpara/devparabind.html
new file mode 100644
index 0000000..51c8ce7
--- /dev/null
+++ b/src/main/resources/templates/restaurant/devpara/devparabind.html
@@ -0,0 +1,106 @@
+<div class="layui-card">
+    <div class="layui-card-header">
+        <h2 class="header-title">设备参数组绑定</h2>
+        <span class="layui-breadcrumb pull-right">
+          <a href="#">设备管理</a>
+          <a><cite>设备参数组绑定</cite></a>
+        </span>
+    </div>
+    <div class="layui-card-body">
+        <div class="layui-form toolbar">
+            搜索:
+            <input id="search-devparabind-searchkey" class="layui-input search-input" maxlength="20" type="text" style="width: 300px;" placeholder="输入参数组名称、设备名称或设备物理ID"/>&emsp;
+            <button id="btn-search-devparabind" class="layui-btn icon-btn" data-type="search"><i class="layui-icon">&#xe615;</i>搜索
+            </button>
+            <button id="search-devparabind-add" class="layui-btn icon-btn" data-type="add"><i class="layui-icon">&#xe654;</i>新 增</button>
+        </div>
+        <table class="layui-table" id="devparabindTable" lay-filter="devparabindTable-filter"></table>
+    </div>
+</div>
+
+
+<!-- 表格操作列 -->
+<script type="text/html" id="devparabind-table-bar">
+    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
+</script>
+
+<script>
+    layui.use(['form', 'table', 'layer', 'admin', 'element'], function () {
+        var form = layui.form;
+        var table = layui.table;
+        var admin = layui.admin;
+        // 渲染表格
+        table.render({
+            elem: '#devparabindTable',
+            url: '[[@{/device/devparabindlist}]]',
+            page: true,
+            cols: [
+                [
+                    {field: 'groupid', title: '参数组编号', width: 150, align: 'center', fixed: 'left', sort: true},
+                    {field: 'groupname', title: '参数组名称', align: 'center', fixed: 'left'},
+                    {field: 'deviceid', title: '设备编号', align: 'center', sort: true},
+                    {field: 'devicename', title: '设备名称', align: 'center'},
+                    {field: 'devphyid', title: '设备物理ID', align: 'center'},
+                    {
+                        field: 'lastsaved', title: '更新时间', align: 'center', templet: function (d) {
+                            return admin.formatDate(d.lastsaved);
+                        }
+                    },
+                    {align: 'center', title: '操作', width: 100, toolbar: '#devparabind-table-bar', fixed: 'right'}
+                ]
+            ]
+        });
+
+        // 搜索按钮点击事件
+        $('#btn-search-devparabind').click(function () {
+            var searchkey = $("#search-devparabind-searchkey").val();
+            table.reload('devparabindTable', {where: {searchkey: searchkey}, page: {curr: 1}});
+        });
+
+        $('#search-devparabind-add').click(function () {
+            admin.popupCenter({
+                title: "新增设备参数组绑定关系",
+                path: '[[@{/device/load4binddevpara}]]',
+                area: '900px',
+                finish: function () {
+                    table.reload('devparabindTable');
+                }
+            });
+        });
+
+        //监听单元格
+        table.on('tool(devparabindTable-filter)', function (obj) {
+            var data = obj.data;
+
+            if('del' == obj.event){
+                layer.confirm('确定要删除设备【'+data.devicename+'】绑定的参数组【'+data.groupname+'】吗?', {
+                    btn: ['确定', '取消']
+                },function(){
+                    layer.closeAll('dialog');
+                    layer.load(2);
+                    admin.go('[[@{/device/deletedevparabind}]]', {
+                        deviceid: data.deviceid,
+                        _csrf: $("meta[name='_csrf_token']").attr("value")
+                    }, function (data) {
+                        console.log(data.code);
+                        layer.closeAll('loading');
+                        if (data.code == 200) {
+                            layer.msg(data.msg, {icon: 1});
+                        } else if (data.code == 401) {
+                            layer.msg(data.msg, {icon: 2, time: 1500}, function () {
+                                location.replace('[[@{/login}]]');
+                            }, 1000);
+                            return;
+                        } else {
+                            layer.msg(data.msg, {icon: 2});
+                        }
+                        table.reload('devparabindTable');
+                    }, function (err) {
+                        admin.errorBack(err)
+                    });
+                });
+            }
+        });
+
+    });
+</script>
\ No newline at end of file
diff --git a/src/main/resources/templates/restaurant/devpara/devparabindform.html b/src/main/resources/templates/restaurant/devpara/devparabindform.html
new file mode 100644
index 0000000..6863470
--- /dev/null
+++ b/src/main/resources/templates/restaurant/devpara/devparabindform.html
@@ -0,0 +1,170 @@
+<form id="devparabind-form" lay-filter="devparabind-form-filter" class="layui-form model-form"
+      style="padding: 30px 25px 10px 25px;">
+    <div class="layui-form-item">
+        <div class="layui-input-inline" style="width: 300px;">
+            <label class="layui-form-label">参数组</label>
+            <div class="layui-input-block">
+                <select lay-verify="required" class="layui-select" id="devparabind-form-select-groupid">
+                    <option th:each="gp:${grouplist}" th:value="${gp.groupid}"
+                            th:text="${gp.groupname}"></option>
+                </select>&emsp;
+            </div>
+        </div>
+    </div>
+
+    <div class="layui-form-item">
+        <label class="layui-form-label">选择设备</label>
+        <div class="layui-input-block" style="border: 1px solid #ddd;border-radius: 4px;padding: 10px;">
+            <div class="layui-card-body">
+                <div class="layui-form toolbar">
+                    <div class="layui-input-inline">
+                        <input id="search-devparabind-form-devgroupid" type="text"
+                               lay-filter="search-devparabind-form-devgroupid-filter" autocomplete="off"
+                               class="layui-input"/>
+                    </div>
+                    设备:
+                    <input id="search-devparabind-form-searchkey" type="text" class="layui-input search-input"
+                           maxlength="20" style="width: 200px;" placeholder="输入设备名称或设备物理ID"/>&emsp;
+                    <button id="btn-search-devparabind-form" class="layui-btn icon-btn"><i
+                            class="layui-icon">&#xe615;</i>搜索
+                    </button>
+                    <button id="btn-reset-devparabind-form" class="layui-btn layui-btn-primary"><i
+                            class="layui-icon"></i>清空
+                    </button>
+
+                    <table class="layui-table" id="devparabindformTable" lay-filter="devparabindformTable-filter"></table>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="layui-form-item model-form-footer">
+        <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
+        <button class="layui-btn" lay-filter="form-submit" lay-submit id="submitbtn">保存</button>
+    </div>
+</form>
+
+<style type="text/css" id="devparabind-form-css">
+    .layui-form-item .layui-form-checkbox[lay-skin=primary] {
+        margin-top: 0;
+    }
+</style>
+
+<script>
+    layui.use(['layer', 'table', 'admin', 'form', 'treeSelect'], function () {
+        var layer = layui.layer;
+        var admin = layui.admin;
+        var form = layui.form;
+        var table = layui.table;
+        var treeSelect = layui.treeSelect;
+        var $ = layui.$
+
+        form.render("select");
+        treeSelect.render({
+            elem: '#search-devparabind-form-devgroupid',
+            data: '[[@{/device/binddevparagrouptree}]]',
+            type: 'get',
+            placeholder: '选择设备组',
+            search: false,
+            style: {
+                folder: {
+                    enable: false
+                },
+                line: {
+                    enable: true
+                }
+            },
+            // 点击回调
+            click: function (d) {
+                var treeNode = d.current;
+                console.log(treeNode);
+                return true;
+            },
+            success: function (d) {
+                console.log(d); // 加载完成后的回调函数
+            }
+        });
+
+        // 渲染表格
+        table.render({
+            elem: '#devparabindformTable',
+            url: '[[@{/device/load4binddevparalist}]]',
+            size: 'sm',
+            height: 350,
+            page: false,
+            cols: [
+                [
+                    {type: 'checkbox', style: "#devparabind-form-css", fixed: 'left'},
+                    {field: 'deviceid', title: '设备编号', align: 'center'},
+                    {field: 'devicename', title: '设备名称', align: 'center'},
+                    {field: 'devphyid', title: '设备物理ID', align: 'center'},
+                    {field: 'devgroupname', title: '设备组', align: 'center'}
+                ]
+            ]
+        });
+
+        // 搜索按钮点击事件
+        $('#btn-search-devparabind-form').click(function () {
+            var devgroupid = $("#search-devparabind-form-devgroupid").val();
+            var searchkey = $("#search-devparabind-form-searchkey").val();
+            table.reload('devparabindformTable', {where: {devgroupid: devgroupid, searchkey: searchkey}});
+        });
+
+        $("#btn-reset-devparabind-form").click(function () {
+            debugger
+            $("#search-devparabind-form-searchkey").val("");
+            $("#search-devparabind-form-devgroupid").val("");
+            treeSelect.revokeNode('search-devparabind-form-devgroupid-filter');
+        });
+
+        form.on('submit(form-submit)', function (data) {
+            var groupid = $("#devparabind-form-select-groupid").val();
+            var checkStatus = table.checkStatus('devparabindformTable');
+            var data = checkStatus.data;
+            var deviceids = [];
+            if ("" == groupid) {
+                layer.msg("请选择参数组", {icon: 2, time: 1500});
+                return;
+            }
+            for (var i = 0; i < data.length; i++) {
+                deviceids.push(data[i].deviceid);
+            }
+            if (deviceids.length < 1) {
+                layer.msg("请选择设备", {icon: 2, time: 1500});
+                return;
+            }
+            debugger
+            console.log(groupid, deviceids);
+            layer.load(2);
+            var token = $("meta[name='_csrf_token']").attr("value");
+            $.ajax({
+                type: "POST",
+                dataType: "json",
+                url: '[[@{/device/addbinddevpara}]]',
+                data: {
+                    "groupid": groupid,
+                    "deviceid[]": deviceids,
+                    "_csrf": token
+                },
+                success: function (result) {
+                    layer.closeAll('loading');
+                    if (result.code == 200) {
+                        layer.msg(result.msg, {icon: 1});
+                        admin.finishPopupCenter();
+                    } else if (result.code == 401) {
+                        layer.msg(result.msg, {icon: 2, time: 1500}, function () {
+                            location.replace('[[@{/login}]]');
+                        }, 1000);
+                        return;
+                    } else {
+                        console.log('err:' + result.code);
+                        layer.msg(result.msg, {icon: 2});
+                    }
+                },
+                error: function (err) {
+                    admin.errorBack(err);
+                }
+            });
+        });
+    });
+</script>
\ No newline at end of file