Commit 8ae7718d8a4b84b1916f5829b046210331e9076e

Authored by 王通
1 parent a9728e40

1.

Showing 26 changed files with 1714 additions and 132 deletions
src/main/java/com/bsth/common/SystemParamKeys.java
... ... @@ -7,6 +7,8 @@ public class SystemParamKeys {
7 7  
8 8 public static final String SPECIAL_ROLES = "special.roles";
9 9  
  10 + public static final String SPECIAL_DAYS = "special.days";
  11 +
10 12 public static final String URL_HTTP_GPS_REAL_CACHE = "url.http.gps.real.cache";
11 13  
12 14 public static final String URL_HTTP_GPS_REAL = "url.http.gps.real";
... ... @@ -40,4 +42,30 @@ public class SystemParamKeys {
40 42 public static final String URL_HTTP_SSO_LOGOUT = "url.http.sso.logout";
41 43  
42 44 public static final String URL_HTTP_SSO_AUTH = "url.http.sso.auth";
43   -}
  45 +
  46 + public static final String URL_HTTP_MAINTENANCE = "url.http.maintenance";
  47 +
  48 + public static final String ENABLED_WHITE_IP = "enabled.white.ip";
  49 +
  50 + public static final String ENABLED_FILTER_AUTHORITY = "enabled.filter.authority";
  51 +
  52 + public static final String URL_HTTP_DVR = "url.http.dvr";
  53 +
  54 + public static final String URL_HTTP_DVR_PWD = "url.http.dvr.pwd";
  55 +
  56 + public static final String DR_SYS_CITY_INTERFACE_URL = "dr.sys.city.interface.url";
  57 +
  58 + public static final String DR_SYS_CITY_INTERFACE_URL1 = "dr.sys.city.interface.url1";
  59 +
  60 + public static final String DR_SYS_CITY_INTERFACE_USER = "dr.sys.city.interface.user";
  61 +
  62 + public static final String DR_SYS_CITY_INTERFACE_PASSWORD = "dr.sys.city.interface.password";
  63 +
  64 + public static final String DR_SYS_CITY_INTERFACE_BAK_URL = "dr.sys.city.interface.bak.url";
  65 +
  66 + public static final String DR_SYS_CITY_INTERFACE_BAK_URL1 = "dr.sys.city.interface.bak.url1";
  67 +
  68 + public static final String DR_SYS_CITY_INTERFACE_BAK_USER = "dr.sys.city.interface.bak.user";
  69 +
  70 + public static final String DR_SYS_CITY_INTERFACE_BAK_PASSWORD = "dr.sys.city.interface.bak.password";
  71 +}
44 72 \ No newline at end of file
... ...
src/main/java/com/bsth/controller/gps/GpsController.java
... ... @@ -233,5 +233,9 @@ public class GpsController {
233 233 public Map<String, Object> allCarsByLine(String lineCode){
234 234 return gpsService.allCarsByLine(lineCode);
235 235 }
236   -
  236 +
  237 + @RequestMapping(value = "/all/fuzzy")
  238 + public Map<String, Object> fuzzy(@RequestParam Map<String, Object> map) {
  239 + return gpsService.fuzzy(map);
  240 + }
237 241 }
... ...
src/main/java/com/bsth/controller/realcontrol/AdminUtilsController.java
... ... @@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
33 33 import org.slf4j.Logger;
34 34 import org.slf4j.LoggerFactory;
35 35 import org.springframework.beans.factory.annotation.Autowired;
  36 +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
36 37 import org.springframework.web.bind.annotation.RequestMapping;
37 38 import org.springframework.web.bind.annotation.RequestParam;
38 39 import org.springframework.web.bind.annotation.RestController;
... ... @@ -403,4 +404,18 @@ public class AdminUtilsController {
403 404  
404 405 return "error";
405 406 }
  407 +
  408 + public static void main(String[] args) {
  409 + String line = "730088,#131597Aa";
  410 + String[] arr = line.split(",");
  411 + if (arr.length == 2) {
  412 + String uname = arr[0], password = arr[1];
  413 + StringBuilder sb = new StringBuilder("update bsth_c_sys_user set password='");
  414 + sb.append(new BCryptPasswordEncoder(4).encode(password));
  415 + sb.append("' where user_name='");
  416 + sb.append(uname);
  417 + sb.append("';");
  418 + System.out.println(sb.toString());
  419 + }
  420 + }
406 421 }
407 422 \ No newline at end of file
... ...
src/main/java/com/bsth/controller/realcontrol/PageForwardingController.java
1   -package com.bsth.controller.realcontrol;
2   -
3   -import com.bsth.data.zndd.voice.UploadVideoServlet;
4   -import com.bsth.entity.sys.Role;
5   -import com.bsth.entity.sys.SysUser;
6   -import com.bsth.security.util.SecurityUtils;
7   -import org.slf4j.Logger;
8   -import org.slf4j.LoggerFactory;
9   -import org.springframework.beans.factory.annotation.Autowired;
10   -import org.springframework.stereotype.Controller;
11   -import org.springframework.web.bind.annotation.PathVariable;
12   -import org.springframework.web.bind.annotation.RequestMapping;
13   -import org.springframework.web.bind.annotation.RequestParam;
14   -import org.springframework.web.servlet.ModelAndView;
15   -
16   -import javax.servlet.ServletException;
17   -import javax.servlet.http.HttpServletRequest;
18   -import javax.servlet.http.HttpServletResponse;
19   -import java.io.IOException;
20   -
21   -/**
22   - * 线调登入页面转发
23   - * Created by panzhao on 2017/1/21.
24   - */
25   -@Controller
26   -@RequestMapping("real_control")
27   -public class PageForwardingController {
28   -
29   - Logger logger = LoggerFactory.getLogger(this.getClass());
30   -
31   - @Autowired
32   - UploadVideoServlet UploadVideoServlet;
33   - @RequestMapping("/v2")
34   - public ModelAndView v2(HttpServletResponse response){
35   - ModelAndView mv = new ModelAndView();
36   - SysUser user = SecurityUtils.getCurrentUser();
37   -
38   - //班次管理员
39   - if(user.getUserName().equals("bcgly")){
40   - mv.setViewName("/real_control_v2/sch_manage/sch_imitate.html");
41   - return mv;
42   - }
43   -
44   - try{
45   - //闵行运管所,直接打开地图页面
46   - if(user.getRoles().size() == 1){
47   - for(Role role : user.getRoles()){
48   - if(role.getCodeName().equals("MH_YGS")){
49   - // 直接重定向
50   - response.sendRedirect("/pages/mapmonitor/alone/wrap.html");
51   - return null;
52   - }
53   - }
54   - }
55   - }catch (Exception e){
56   - logger.error("", e);
57   - }
58   -
59   - //正常线调主页
60   - mv.setViewName("/real_control_v2/main.html");
61   - return mv;
62   - }
63   -
64   - @RequestMapping(value = "zndd/do/{line}")
65   - public void doPost(@PathVariable("line") String line, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
66   - UploadVideoServlet.doPost(line,request,response);
67   - }
68   -}
  1 +package com.bsth.controller.realcontrol;
  2 +
  3 +import com.bsth.data.zndd.voice.UploadVideoServlet;
  4 +import com.bsth.entity.sys.Role;
  5 +import com.bsth.entity.sys.SysUser;
  6 +import com.bsth.security.util.SecurityUtils;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.stereotype.Controller;
  11 +import org.springframework.web.bind.annotation.PathVariable;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RequestParam;
  14 +import org.springframework.web.servlet.ModelAndView;
  15 +
  16 +import javax.servlet.ServletException;
  17 +import javax.servlet.http.HttpServletRequest;
  18 +import javax.servlet.http.HttpServletResponse;
  19 +import java.io.IOException;
  20 +
  21 +/**
  22 + * 线调登入页面转发
  23 + * Created by panzhao on 2017/1/21.
  24 + */
  25 +@Controller
  26 +@RequestMapping("real_control")
  27 +public class PageForwardingController {
  28 +
  29 + Logger logger = LoggerFactory.getLogger(this.getClass());
  30 +
  31 + @Autowired
  32 + UploadVideoServlet UploadVideoServlet;
  33 + @RequestMapping("/v2")
  34 + public ModelAndView v2(HttpServletResponse response){
  35 + ModelAndView mv = new ModelAndView();
  36 + SysUser user = SecurityUtils.getCurrentUser();
  37 +
  38 + //班次管理员
  39 + if(user.getUserName().equals("bcgly")){
  40 + mv.setViewName("/real_control_v2/sch_manage/sch_imitate.html");
  41 + return mv;
  42 + }
  43 +
  44 + try{
  45 + //闵行运管所,直接打开地图页面
  46 + if(user.getRoles().size() == 1){
  47 + for(Role role : user.getRoles()){
  48 + if(role.getCodeName().equals("MH_YGS")){
  49 + // 直接重定向
  50 + response.sendRedirect("/pages/mapmonitor/alone/wrap.html");
  51 + return null;
  52 + }
  53 + }
  54 + }
  55 + }catch (Exception e){
  56 + logger.error("", e);
  57 + }
  58 +
  59 + //正常线调主页
  60 + mv.setViewName("/real_control_v2/main.html");
  61 + return mv;
  62 + }
  63 +
  64 + @RequestMapping("/demo")
  65 + public ModelAndView demo(HttpServletResponse response){
  66 + ModelAndView mv = new ModelAndView();
  67 + mv.setViewName("/pages/demo.html");
  68 + return mv;
  69 + }
  70 +
  71 + @RequestMapping(value = "zndd/do/{line}")
  72 + public void doPost(@PathVariable("line") String line, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  73 + UploadVideoServlet.doPost(line,request,response);
  74 + }
  75 +}
... ...
src/main/java/com/bsth/controller/realcontrol/ServiceDataInterface.java
... ... @@ -108,6 +108,7 @@ public class ServiceDataInterface {
108 108 map.put("remarks", sch.getRemark());
109 109 map.put("status", sch.getStatus());
110 110 map.put("realExecDate", sch.getRealExecDate());
  111 + map.put("lineRegion", sch.getLineRegion());
111 112  
112 113 //放站班次,放到的站点
113 114 map.put("majorStationName", sch.getMajorStationName());
... ...
src/main/java/com/bsth/data/SystemParamCache.java
... ... @@ -21,6 +21,10 @@ public class SystemParamCache implements InitializingBean {
21 21 return systemParamService1.getValue(SystemParamKeys.SPECIAL_ROLES);
22 22 }
23 23  
  24 + public static String getSpecialDays() {
  25 + return systemParamService1.getValue(SystemParamKeys.SPECIAL_DAYS);
  26 + }
  27 +
24 28 public static String getUrlHttpGpsRealCache() {
25 29 return systemParamService1.getValue(SystemParamKeys.URL_HTTP_GPS_REAL_CACHE);
26 30 }
... ... @@ -85,11 +89,62 @@ public class SystemParamCache implements InitializingBean {
85 89 return systemParamService1.getValue(SystemParamKeys.URL_HTTP_SSO_LOGOUT);
86 90 }
87 91  
88   - public static final String URL_HTTP_SSO_AUTH = "url.http.sso.auth";
89 92 public static String getUrlHttpSsoAuth() {
90 93 return systemParamService1.getValue(SystemParamKeys.URL_HTTP_SSO_AUTH);
91 94 }
92 95  
  96 + public static String getUrlHttpMaintenance() {
  97 + return systemParamService1.getValue(SystemParamKeys.URL_HTTP_MAINTENANCE);
  98 + }
  99 +
  100 + public static boolean getEnabledWhiteIp() {
  101 + return Boolean.parseBoolean(systemParamService1.getValue(SystemParamKeys.ENABLED_WHITE_IP));
  102 + }
  103 +
  104 + public static boolean getEnableFilterAuthority() {
  105 + return Boolean.parseBoolean(systemParamService1.getValue(SystemParamKeys.ENABLED_FILTER_AUTHORITY));
  106 + }
  107 +
  108 + public static String getUrlHttpDvr() {
  109 + return systemParamService1.getValue(SystemParamKeys.URL_HTTP_DVR);
  110 + }
  111 +
  112 + public static String getUrlHttpDvrPwd() {
  113 + return systemParamService1.getValue(SystemParamKeys.URL_HTTP_DVR_PWD);
  114 + }
  115 +
  116 + public static String getDrSysCityInterfaceUrl() {
  117 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_URL);
  118 + }
  119 +
  120 + public static String getDrSysCityInterfaceUrl1() {
  121 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_URL1);
  122 + }
  123 +
  124 + public static String getDrSysCityInterfaceUser() {
  125 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_USER);
  126 + }
  127 +
  128 + public static String getDrSysCityInterfacePassword() {
  129 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_PASSWORD);
  130 + }
  131 +
  132 + public static String getDrSysCityInterfaceBakUrl() {
  133 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_BAK_URL);
  134 + }
  135 +
  136 + public static String getDrSysCityInterfaceBakUrl1() {
  137 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_BAK_URL1);
  138 + }
  139 +
  140 + public static String getDrSysCityInterfaceBakUser() {
  141 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_BAK_USER);
  142 + }
  143 +
  144 + public static String getDrSysCityInterfaceBakPassword() {
  145 + return systemParamService1.getValue(SystemParamKeys.DR_SYS_CITY_INTERFACE_BAK_PASSWORD);
  146 + }
  147 +
93 148 @Override
94 149 public void afterPropertiesSet() throws Exception {
95 150 systemParamService1 = systemParamService;
... ...
src/main/java/com/bsth/data/car_out_info/CarOutInfoHandler.java
... ... @@ -117,8 +117,8 @@ public class CarOutInfoHandler {
117 117 //删除
118 118 jdbcTemplate.update("delete from bsth_t_clfcxxb");
119 119 //重新写入
120   - jdbcTemplate.batchUpdate("insert into bsth_t_clfcxxb(rq, line_code, line_name, lp_name, lp_sn, dfsj, nbbm, cph, bc_type, end_station_name, updown, jGh, jName, remarks, sn, sch)" +
121   - " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new BatchPreparedStatementSetter() {
  120 + jdbcTemplate.batchUpdate("insert into bsth_t_clfcxxb(rq, line_code, line_name, lp_name, lp_sn, dfsj, nbbm, cph, bc_type, end_station_name, updown, jGh, jName, remarks, sn, sch, line_region)" +
  121 + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new BatchPreparedStatementSetter() {
122 122 @Override
123 123 public void setValues(PreparedStatement ps, int i) throws SQLException {
124 124 ScheduleRealInfo sch = pstList.get(i);
... ... @@ -138,6 +138,7 @@ public class CarOutInfoHandler {
138 138 ps.setString(14, sch.getRemarks());
139 139 ps.setInt(15, sch.getFcpSn());
140 140 ps.setLong(16, sch.getId());
  141 + ps.setString(17, sch.getLineRegion());
141 142 }
142 143  
143 144 @Override
... ...
src/main/java/com/bsth/entity/Detour.java 0 → 100644
  1 +package com.bsth.entity;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonIgnore;
  4 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  5 +import org.hibernate.annotations.DynamicInsert;
  6 +import org.hibernate.annotations.DynamicUpdate;
  7 +
  8 +import javax.persistence.*;
  9 +
  10 +/**
  11 + * 绕改道实体类
  12 + * 对应数据库表 bsth_c_detour
  13 + */
  14 +@Entity
  15 +@Table(name = "bsth_c_detour")
  16 +@DynamicInsert
  17 +@DynamicUpdate
  18 +@JsonIgnoreProperties(ignoreUnknown = true)
  19 +public class Detour {
  20 +
  21 + // 主键
  22 + @Id
  23 + @GeneratedValue(strategy = GenerationType.IDENTITY)
  24 + private Integer id;
  25 +
  26 + // 线路
  27 + private Integer line;
  28 +
  29 + // 版本号
  30 + @JsonIgnore
  31 + private Integer versions;
  32 +
  33 + // 上下行
  34 + private Integer direction;
  35 +
  36 + // 起点编码
  37 + @JsonIgnore
  38 + private String startStationCode;
  39 +
  40 + @Transient
  41 + private int startIdx;
  42 +
  43 + // 终点编码
  44 + @JsonIgnore
  45 + private String terminalStationCode;
  46 +
  47 + @Transient
  48 + private int terminalIdx;
  49 +
  50 + // 路段wgs84点位
  51 + private String points;
  52 +
  53 + public Integer getId() {
  54 + return id;
  55 + }
  56 +
  57 + public void setId(Integer id) {
  58 + this.id = id;
  59 + }
  60 +
  61 + public Integer getLine() {
  62 + return line;
  63 + }
  64 +
  65 + public void setLine(Integer line) {
  66 + this.line = line;
  67 + }
  68 +
  69 + public Integer getVersions() {
  70 + return versions;
  71 + }
  72 +
  73 + public void setVersions(Integer versions) {
  74 + this.versions = versions;
  75 + }
  76 +
  77 + public Integer getDirection() {
  78 + return direction;
  79 + }
  80 +
  81 + public void setDirection(Integer direction) {
  82 + this.direction = direction;
  83 + }
  84 +
  85 + public String getStartStationCode() {
  86 + return startStationCode;
  87 + }
  88 +
  89 + public void setStartStationCode(String startStationCode) {
  90 + this.startStationCode = startStationCode;
  91 + }
  92 +
  93 + public int getStartIdx() {
  94 + return startIdx;
  95 + }
  96 +
  97 + public void setStartIdx(int startIdx) {
  98 + this.startIdx = startIdx;
  99 + }
  100 +
  101 + public String getTerminalStationCode() {
  102 + return terminalStationCode;
  103 + }
  104 +
  105 + public void setTerminalStationCode(String terminalStationCode) {
  106 + this.terminalStationCode = terminalStationCode;
  107 + }
  108 +
  109 + public int getTerminalIdx() {
  110 + return terminalIdx;
  111 + }
  112 +
  113 + public void setTerminalIdx(int terminalIdx) {
  114 + this.terminalIdx = terminalIdx;
  115 + }
  116 +
  117 + public String getPoints() {
  118 + return points;
  119 + }
  120 +
  121 + public void setPoints(String points) {
  122 + this.points = points;
  123 + }
  124 +}
0 125 \ No newline at end of file
... ...
src/main/java/com/bsth/entity/LsStationRoute.java
... ... @@ -165,6 +165,11 @@ public class LsStationRoute {
165 165 @Transient
166 166 private String centerPointWgsWkt;
167 167  
  168 + /**
  169 + * 绕改道当前站点对应的后一站点
  170 + */
  171 + private Integer detourStationRoute;
  172 +
168 173 public Integer getId() {
169 174 return id;
170 175 }
... ... @@ -440,4 +445,12 @@ public class LsStationRoute {
440 445  
441 446 return centerPointWgsWkt;
442 447 }
  448 +
  449 + public Integer getDetourStationRoute() {
  450 + return detourStationRoute;
  451 + }
  452 +
  453 + public void setDetourStationRoute(Integer detourStationRoute) {
  454 + this.detourStationRoute = detourStationRoute;
  455 + }
443 456 }
444 457 \ No newline at end of file
... ...
src/main/java/com/bsth/entity/StationRoute.java
... ... @@ -198,6 +198,11 @@ public class StationRoute {
198 198 @Transient
199 199 private String centerPointWgsWkt;
200 200  
  201 + /**
  202 + * 绕改道当前站点对应的后一站点
  203 + */
  204 + private Integer detourStationRoute;
  205 +
201 206 public Integer getId() {
202 207 return id;
203 208 }
... ... @@ -473,4 +478,12 @@ public class StationRoute {
473 478  
474 479 return centerPointWgsWkt;
475 480 }
  481 +
  482 + public Integer getDetourStationRoute() {
  483 + return detourStationRoute;
  484 + }
  485 +
  486 + public void setDetourStationRoute(Integer detourStationRoute) {
  487 + this.detourStationRoute = detourStationRoute;
  488 + }
476 489 }
477 490 \ No newline at end of file
... ...
src/main/java/com/bsth/entity/transport_capability/TransportCapability.java 0 → 100644
  1 +package com.bsth.entity.transport_capability;
  2 +
  3 +/**
  4 + * 运能运力
  5 + */
  6 +public class TransportCapability {
  7 +
  8 + private String nbbm;
  9 +
  10 + private String lineCode;
  11 +
  12 + private String lineName;
  13 +
  14 + private int upDown;
  15 +
  16 + private String qdzArrDatejh;
  17 +
  18 + private long dfsjT;
  19 +
  20 + private String dfsj;
  21 +
  22 + private float lon;
  23 +
  24 + private float lat;
  25 +
  26 + public String getNbbm() {
  27 + return nbbm;
  28 + }
  29 +
  30 + public void setNbbm(String nbbm) {
  31 + this.nbbm = nbbm;
  32 + }
  33 +
  34 + public String getLineCode() {
  35 + return lineCode;
  36 + }
  37 +
  38 + public void setLineCode(String lineCode) {
  39 + this.lineCode = lineCode;
  40 + }
  41 +
  42 + public String getLineName() {
  43 + return lineName;
  44 + }
  45 +
  46 + public void setLineName(String lineName) {
  47 + this.lineName = lineName;
  48 + }
  49 +
  50 + public int getUpDown() {
  51 + return upDown;
  52 + }
  53 +
  54 + public void setUpDown(int upDown) {
  55 + this.upDown = upDown;
  56 + }
  57 +
  58 + public String getQdzArrDatejh() {
  59 + return qdzArrDatejh;
  60 + }
  61 +
  62 + public void setQdzArrDatejh(String qdzArrDatejh) {
  63 + this.qdzArrDatejh = qdzArrDatejh;
  64 + }
  65 +
  66 + public long getDfsjT() {
  67 + return dfsjT;
  68 + }
  69 +
  70 + public void setDfsjT(long dfsjT) {
  71 + this.dfsjT = dfsjT;
  72 + }
  73 +
  74 + public String getDfsj() {
  75 + return dfsj;
  76 + }
  77 +
  78 + public void setDfsj(String dfsj) {
  79 + this.dfsj = dfsj;
  80 + }
  81 +
  82 + public float getLon() {
  83 + return lon;
  84 + }
  85 +
  86 + public void setLon(float lon) {
  87 + this.lon = lon;
  88 + }
  89 +
  90 + public float getLat() {
  91 + return lat;
  92 + }
  93 +
  94 + public void setLat(float lat) {
  95 + this.lat = lat;
  96 + }
  97 +}
... ...
src/main/java/com/bsth/repository/DetourRepository.java 0 → 100644
  1 +package com.bsth.repository;
  2 +
  3 +import com.bsth.entity.Detour;
  4 +
  5 +/**
  6 + * 绕改道 Repository 接口
  7 + */
  8 +public interface DetourRepository extends BaseRepository<Detour, Integer> {
  9 +
  10 +}
0 11 \ No newline at end of file
... ...
src/main/java/com/bsth/service/DetourService.java 0 → 100644
  1 +package com.bsth.service;
  2 +
  3 +import com.bsth.entity.Detour;
  4 +
  5 +/**
  6 + * 绕改道 Service 接口
  7 + */
  8 +public interface DetourService extends BaseService<Detour, Integer> {
  9 +
  10 +}
0 11 \ No newline at end of file
... ...
src/main/java/com/bsth/service/gps/GpsService.java
... ... @@ -47,4 +47,6 @@ public interface GpsService {
47 47 Map<String, Object> Pagequery(Map<String, Object> map);
48 48  
49 49 Map<String,Object> allCarsByLine(String lineCode);
  50 +
  51 + Map<String, Object> fuzzy(Map<String, Object> map);
50 52 }
... ...
src/main/java/com/bsth/service/gps/GpsServiceImpl.java
... ... @@ -1576,6 +1576,16 @@ public class GpsServiceImpl implements GpsService {
1576 1576 return map;
1577 1577 }
1578 1578  
  1579 + @Override
  1580 + public Map<String, Object> fuzzy(Map<String, Object> map) {
  1581 + List<String> nbbs = BasicData.getServiceNbbms();
  1582 +
  1583 +
  1584 + dayOfSchedule.executeCurr("").getQdzArrDatejh();
  1585 + dayOfSchedule.next(null);
  1586 + return Collections.emptyMap();
  1587 + }
  1588 +
1579 1589 static List<GpsSpeed> findAll(Map<String, Object> map) {
1580 1590 Connection conn = null;
1581 1591 PreparedStatement ps = null;
... ...
src/main/java/com/bsth/service/impl/DetourServiceImpl.java 0 → 100644
  1 +package com.bsth.service.impl;
  2 +
  3 +import com.bsth.entity.Detour;
  4 +import com.bsth.repository.DetourRepository;
  5 +import com.bsth.service.DetourService;
  6 +import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * 绕改道 Service 实现类
  11 + */
  12 +@Service
  13 +public class DetourServiceImpl extends BaseServiceImpl<Detour, Integer> implements DetourService {
  14 +
  15 + @Autowired
  16 + private DetourRepository detourRepository;
  17 +
  18 +}
0 19 \ No newline at end of file
... ...
src/main/java/com/bsth/service/impl/LsStationRouteServiceImpl.java
1 1 package com.bsth.service.impl;
2 2  
3 3 import com.bsth.annotation.BusinessDescription;
  4 +import com.bsth.data.gpsdata_v2.utils.GeoUtils;
4 5 import com.bsth.entity.*;
5 6 import com.bsth.entity.search.CustomerSpecs;
6 7 import com.bsth.repository.*;
7 8 import com.bsth.service.LsStationRouteService;
8   -import com.bsth.util.CoordinateConverter;
9 9 import com.bsth.util.CustomBeanUtils;
10 10 import com.bsth.util.GeoConverter;
11   -import com.fasterxml.jackson.core.JsonProcessingException;
12   -import com.fasterxml.jackson.databind.ObjectMapper;
13 11 import org.geolatte.geom.LineString;
14   -import org.geolatte.geom.Point;
15 12 import org.geolatte.geom.Polygon;
16 13 import org.geolatte.geom.codec.Wkt;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
17 16 import org.springframework.beans.factory.annotation.Autowired;
18 17 import org.springframework.data.domain.Sort;
19 18 import org.springframework.jdbc.core.BatchPreparedStatementSetter;
... ... @@ -32,6 +31,8 @@ import java.util.*;
32 31 @Service
33 32 public class LsStationRouteServiceImpl extends BaseServiceImpl<LsStationRoute, Integer> implements LsStationRouteService {
34 33  
  34 + private final static Logger log = LoggerFactory.getLogger(LsStationRouteServiceImpl.class);
  35 +
35 36 @Autowired
36 37 private LsStationRouteRepository lsStationRouteRepository;
37 38  
... ... @@ -59,6 +60,9 @@ public class LsStationRouteServiceImpl extends BaseServiceImpl&lt;LsStationRoute, I
59 60 @Autowired
60 61 private JdbcTemplate jdbcTemplate;
61 62  
  63 + @Autowired
  64 + private DetourRepository detourRepository;
  65 +
62 66 @Override
63 67 public Iterable<LsStationRoute> findAllByParams(Map<String, Object> map) {
64 68 List<Sort.Order> orders = new ArrayList<>();
... ... @@ -318,9 +322,201 @@ public class LsStationRouteServiceImpl extends BaseServiceImpl&lt;LsStationRoute, I
318 322 refreshCurrent(stationRoute1.getLine().getId(), currentVersion);
319 323 }
320 324  
  325 + // 处理绕改道逻辑
  326 + handleDetour(stationRoute1);
  327 +
321 328 return new HashMap<>();
322 329 }
323 330  
  331 + private void handleDetour(LsStationRoute stationRoute) {
  332 + Integer detourStationRouteId = stationRoute.getDetourStationRoute();
  333 + if (detourStationRouteId == null) {
  334 + return;
  335 + }
  336 +
  337 + // 获取终点站点路由
  338 + LsStationRoute endStationRoute = lsStationRouteRepository.findById(detourStationRouteId)
  339 + .orElseThrow(() -> new IllegalArgumentException("未找到对应的终点站点路由,ID:" + detourStationRouteId));
  340 +
  341 + // 获取起点和终点的WGS坐标
  342 + org.geolatte.geom.Point startPoint = stationRoute.getCenterPointWgs();
  343 + org.geolatte.geom.Point endPoint = endStationRoute.getCenterPointWgs();
  344 + if (startPoint == null || endPoint == null) {
  345 + throw new IllegalArgumentException("起点或终点站点坐标信息不完整");
  346 + }
  347 +
  348 + // 转换为内部Geo.Point
  349 + com.bsth.util.Geo.Point startGeoPoint = toGeoPoint(startPoint);
  350 + com.bsth.util.Geo.Point endGeoPoint = toGeoPoint(endPoint);
  351 +
  352 + // 获取线路、版本号、上下行信息
  353 + Integer lineId = Optional.ofNullable(stationRoute.getLine())
  354 + .map(Line::getId)
  355 + .orElseThrow(() -> new IllegalArgumentException("站点路由关联的线路信息缺失"));
  356 + Integer versions = stationRoute.getVersions();
  357 + Integer directions = stationRoute.getDirections();
  358 +
  359 + // 查询所有路段路由(按路段编码升序)
  360 + Map<String, Object> params = new HashMap<>();
  361 + params.put("line.id_eq", lineId);
  362 + params.put("versions_eq", versions);
  363 + params.put("directions_eq", directions);
  364 + params.put("destroy_eq", 0);
  365 + List<LsSectionRoute> sectionRoutes = lsSectionRouteRepository.findAll(
  366 + new CustomerSpecs<>(params),
  367 + Sort.by(Sort.Direction.ASC, "sectionrouteCode")
  368 + );
  369 +
  370 + // 第一步:找到起点和终点所在的路段及索引
  371 + LsSectionRoute startSection = null;
  372 + LsSectionRoute endSection = null;
  373 + int startIndex = -1;
  374 + int endIndex = -1;
  375 + double minStartDistance = Double.MAX_VALUE;
  376 + double minEndDistance = Double.MAX_VALUE;
  377 +
  378 + for (int i = 0; i < sectionRoutes.size(); i++) {
  379 + LsSectionRoute sectionRoute = sectionRoutes.get(i);
  380 + Section section = sectionRoute.getSection();
  381 + if (section == null || section.getGsectionVector() == null) {
  382 + continue;
  383 + }
  384 + LineString lineString = section.getGsectionVector();
  385 +
  386 + // 计算起点到当前路段的最小距离
  387 + double startDist = calcMinDistanceToLine(lineString, startGeoPoint);
  388 + if (startDist < minStartDistance) {
  389 + minStartDistance = startDist;
  390 + startSection = sectionRoute;
  391 + startIndex = i;
  392 + }
  393 +
  394 + // 计算终点到当前路段的最小距离
  395 + double endDist = calcMinDistanceToLine(lineString, endGeoPoint);
  396 + if (endDist < minEndDistance) {
  397 + minEndDistance = endDist;
  398 + endSection = sectionRoute;
  399 + endIndex = i;
  400 + }
  401 + }
  402 +
  403 + if (startSection == null || endSection == null) {
  404 + log.warn("未能找到起点或终点所在的路段,起点ID:{},终点ID:{}",
  405 + stationRoute.getId(), endStationRoute.getId());
  406 + return;
  407 + }
  408 +
  409 + // 第二步:确定中间经过的路段(排除起点和终点路段)
  410 + List<LsSectionRoute> passingSections = new ArrayList<>();
  411 + if (startIndex >= 0 && endIndex >= 0) {
  412 + int low = Math.min(startIndex, endIndex);
  413 + int high = Math.max(startIndex, endIndex);
  414 + // 只添加严格介于两者之间的路段
  415 + for (int i = low + 1; i < high; i++) {
  416 + passingSections.add(sectionRoutes.get(i));
  417 + }
  418 + }
  419 +
  420 + // 检查路段数(超过15视为异常)
  421 + if (passingSections.size() > 13) {
  422 + log.error("绕改道路径过长,起点路段:{},终点路段:{},中间路段数:{}",
  423 + startSection.getId(), endSection.getId(), passingSections.size());
  424 + throw new RuntimeException("过长的绕改道路径(超过13个中间路段)");
  425 + }
  426 +
  427 + // 构建绕改道路径
  428 + Detour detour = new Detour();
  429 + detour.setLine(lineId);
  430 + detour.setVersions(versions);
  431 + detour.setDirection(directions);
  432 + detour.setStartStationCode(stationRoute.getStationCode());
  433 + detour.setTerminalStationCode(endStationRoute.getStationCode());
  434 +
  435 + String points = buildPoints(startPoint, passingSections, endPoint);
  436 + try {
  437 + detour.setPoints(points);
  438 + detourRepository.save(detour);
  439 + log.info("绕改道路径保存成功,线路:{},版本:{},方向:{}", lineId, versions, directions);
  440 + } catch (Exception e) {
  441 + log.error("保存绕改道失败,points:{}", points, e);
  442 + throw new RuntimeException("保存绕改道路径失败", e);
  443 + }
  444 + }
  445 +
  446 + /**
  447 + * 将org.geolatte.geom.Point转换为内部Geo.Point
  448 + */
  449 + private com.bsth.util.Geo.Point toGeoPoint(org.geolatte.geom.Point point) {
  450 + return new com.bsth.util.Geo.Point(
  451 + point.getPosition().getCoordinate(0),
  452 + point.getPosition().getCoordinate(1)
  453 + );
  454 + }
  455 +
  456 + /**
  457 + * 将org.geolatte.geom.Position转换为内部Geo.Point
  458 + */
  459 + private com.bsth.util.Geo.Point toGeoPoint(org.geolatte.geom.Position position) {
  460 + return new com.bsth.util.Geo.Point(
  461 + position.getCoordinate(0),
  462 + position.getCoordinate(1)
  463 + );
  464 + }
  465 +
  466 + /**
  467 + * 计算点到LineString的最小距离(遍历每个线段)
  468 + * 注意:使用getPositionN获取位置,LineString无getPointN方法
  469 + */
  470 + private double calcMinDistanceToLine(LineString lineString,
  471 + com.bsth.util.Geo.Point geoPoint) {
  472 + double minDist = Double.MAX_VALUE;
  473 + int numPoints = lineString.getNumPositions(); // 获取点数
  474 + for (int i = 0; i < numPoints - 1; i++) {
  475 + org.geolatte.geom.Position p1 = lineString.getPositionN(i);
  476 + org.geolatte.geom.Position p2 = lineString.getPositionN(i + 1);
  477 + com.bsth.util.Geo.Point geoP1 = toGeoPoint(p1);
  478 + com.bsth.util.Geo.Point geoP2 = toGeoPoint(p2);
  479 + double dist = GeoUtils.getDistanceFromLine(geoP1, geoP2, geoPoint);
  480 + minDist = Math.min(minDist, dist);
  481 + }
  482 + return minDist;
  483 + }
  484 +
  485 + /**
  486 + * 构建坐标点 包含起点、中间路段中点、终点
  487 + */
  488 + private String buildPoints(org.geolatte.geom.Point startPoint,
  489 + List<LsSectionRoute> passingSections,
  490 + org.geolatte.geom.Point endPoint) {
  491 + StringBuilder wktBuilder = new StringBuilder();
  492 + // 添加起点
  493 + wktBuilder.append(startPoint.getPosition().getCoordinate(0))
  494 + .append(" ")
  495 + .append(startPoint.getPosition().getCoordinate(1));
  496 +
  497 + // 添加中间路段的中点
  498 + for (LsSectionRoute sectionRoute : passingSections) {
  499 + Section section = sectionRoute.getSection();
  500 + if (section != null && section.getGsectionVector() != null) {
  501 + LineString lineString = section.getGsectionVector();
  502 + int numPoints = lineString.getNumPositions();
  503 + int midIndex = numPoints / 2; // 取中间索引点
  504 + org.geolatte.geom.Position midPos = lineString.getPositionN(midIndex);
  505 + wktBuilder.append(",")
  506 + .append(midPos.getCoordinate(0))
  507 + .append(" ")
  508 + .append(midPos.getCoordinate(1));
  509 + }
  510 + }
  511 +
  512 + // 添加终点
  513 + wktBuilder.append(",")
  514 + .append(endPoint.getPosition().getCoordinate(0))
  515 + .append(" ")
  516 + .append(endPoint.getPosition().getCoordinate(1));
  517 + return wktBuilder.toString();
  518 + }
  519 +
324 520 @Transactional
325 521 @Override
326 522 public Map<String, Object> exchangeDirection(int lineId, int version) {
... ...
src/main/java/com/bsth/service/impl/StationRouteServiceImpl.java
... ... @@ -509,8 +509,8 @@ public class StationRouteServiceImpl extends BaseServiceImpl&lt;StationRoute, Integ
509 509 clientUtils.uploadFile(url, port, username, password, remotePath + "/voice/", textFileName, input);
510 510 String linePath = String.format(linePathPattern, lineId), voicePath = String.format("%s%s.zip", linePath, lineId);
511 511 // 报站文件上传(全程)
512   - clientUtils.deleteFtpFile(url, port, username, password, remotePath + "/voice/", String.format("%s.zip", line.getLineCode()));
513   - clientUtils.FTPUpLoadFromDisk(new File(voicePath), String.format("%s.zip", line.getLineCode()), url, port, username, password, remotePath + "/voice/");
  512 +// clientUtils.deleteFtpFile(url, port, username, password, remotePath + "/voice/", String.format("%s.zip", line.getLineCode()));
  513 +// clientUtils.FTPUpLoadFromDisk(new File(voicePath), String.format("%s.zip", line.getLineCode()), url, port, username, password, remotePath + "/voice/");
514 514  
515 515 // 线路区间
516 516 if (lineRegions.size() > 0) {
... ... @@ -1305,7 +1305,7 @@ public class StationRouteServiceImpl extends BaseServiceImpl&lt;StationRoute, Integ
1305 1305 // 子线路路径
1306 1306 String linePath = String.format(linePathPattern, lineId);
1307 1307 String subLinePath = String.format("%s%s/", linePath, subLineId);
1308   - List<String> arr = Arrays.asList(commonPath + "cn_origin_1.mp3", linePath + String.format("cn/%03d.mp3", 0), commonPath + "cn_origin_2.mp3", linePath + String.format("cn/%03d.mp3", terminal.getStationLevel()), commonPath + "sh_origin_1.mp3", linePath + String.format("sh/%03d.mp3", 0), commonPath + "sh_origin_2.mp3", linePath + String.format("cn/%03d.mp3", terminal.getStationLevel()), commonPath + "cn_start_1.mp3", linePath + String.format("cn/%03d.mp3", current.getStationLevel()), commonPath + "cn_start_2.mp3", commonPath + "sh_start_1.mp3", linePath + String.format("sh/%03d.mp3", current.getStationLevel()), commonPath + "sh_start_2.mp3", commonPath + "en_1.mp3", linePath + String.format("en/%03d.mp3", current.getStationLevel()), commonPath + "cn_start_3.mp3", commonPath + "sh_start_3.mp3");
  1308 + List<String> arr = Arrays.asList(commonPath + "cn_origin_1.mp3", linePath + String.format("cn/%03d.mp3", 0), commonPath + "cn_origin_2.mp3", linePath + String.format("cn/%03d.mp3", terminal.getStationLevel()), commonPath + "sh_origin_1.mp3", linePath + String.format("sh/%03d.mp3", 0), commonPath + "sh_origin_2.mp3", linePath + String.format("sh/%03d.mp3", terminal.getStationLevel()), commonPath + "cn_start_1.mp3", linePath + String.format("cn/%03d.mp3", current.getStationLevel()), commonPath + "cn_start_2.mp3", commonPath + "sh_start_1.mp3", linePath + String.format("sh/%03d.mp3", current.getStationLevel()), commonPath + "sh_start_2.mp3", commonPath + "en_1.mp3", linePath + String.format("en/%03d.mp3", current.getStationLevel()), commonPath + "cn_start_3.mp3", commonPath + "sh_start_3.mp3");
1309 1309 List<String> inputPaths = new ArrayList<>();
1310 1310 for (String path : arr) {
1311 1311 for (String lang : languages) {
... ...
src/main/java/com/bsth/service/impl/TrafficManageServiceImpl.java
1 1 package com.bsth.service.impl;
2 2  
3 3 import com.bsth.data.BasicData;
  4 +import com.bsth.data.SystemParamCache;
4 5 import com.bsth.email.SendEmailController;
5 6 import com.bsth.email.entity.EmailBean;
6 7 import com.bsth.entity.*;
... ... @@ -23,9 +24,9 @@ import com.bsth.util.CoordinateConverter;
23 24 import com.bsth.util.IpUtils;
24 25 import com.bsth.util.TimeUtils;
25 26 import com.bsth.util.db.DBUtils_MS;
26   -import com.bsth.webService.trafficManage.org.tempuri.Results;
27   -import com.bsth.webService.trafficManage.org.tempuri.WebServiceLocator;
28   -import com.bsth.webService.trafficManage.org.tempuri.WebServiceSoap;
  27 +import com.bsth.webService.trafficManage.up.org.tempuri.Results;
  28 +import com.bsth.webService.trafficManage.up.org.tempuri.WebServiceLocator;
  29 +import com.bsth.webService.trafficManage.up.org.tempuri.WebServiceSoap;
29 30 import org.apache.commons.lang.StringEscapeUtils;
30 31 import org.apache.commons.lang.StringUtils;
31 32 import org.apache.commons.lang.time.DateUtils;
... ... @@ -33,6 +34,7 @@ import org.joda.time.DateTime;
33 34 import org.slf4j.Logger;
34 35 import org.slf4j.LoggerFactory;
35 36 import org.springframework.beans.factory.annotation.Autowired;
  37 +import org.springframework.beans.factory.annotation.Value;
36 38 import org.springframework.data.domain.Sort;
37 39 import org.springframework.data.domain.Sort.Direction;
38 40 import org.springframework.security.core.context.SecurityContextHolder;
... ... @@ -135,11 +137,31 @@ public class TrafficManageServiceImpl implements TrafficManageService{
135 137 private LineVersionsRepository lineVersionsRepository;
136 138  
137 139 // 运管处上传接口
138   - private com.bsth.webService.trafficManage.up.org.tempuri.WebServiceSoap webServiceSoapUp;
139   - private WebServiceSoap ssop ;
  140 + private WebServiceSoap ssop,ssop1;
  141 + private WebServiceSoap ssopBak,ssopBak1;
140 142 {
141 143 try {
142   - ssop = new WebServiceLocator().getWebServiceSoap();
  144 + WebServiceLocator locator = new WebServiceLocator();
  145 + String url = SystemParamCache.getDrSysCityInterfaceUrl();
  146 + if (StringUtils.isNotEmpty(url)) {
  147 + locator.setWebServiceSoapEndpointAddress(url);
  148 + ssop = locator.getWebServiceSoap();
  149 + }
  150 + url = SystemParamCache.getDrSysCityInterfaceUrl1();
  151 + if (StringUtils.isNotEmpty(url)) {
  152 + locator.setWebServiceSoapEndpointAddress(url);
  153 + ssop1 = locator.getWebServiceSoap();
  154 + }
  155 + url = SystemParamCache.getDrSysCityInterfaceBakUrl();
  156 + if (StringUtils.isNotEmpty(url)) {
  157 + locator.setWebServiceSoapEndpointAddress(url);
  158 + ssopBak = locator.getWebServiceSoap();
  159 + }
  160 + url = SystemParamCache.getDrSysCityInterfaceBakUrl1();
  161 + if (StringUtils.isNotEmpty(url)) {
  162 + locator.setWebServiceSoapEndpointAddress(url);
  163 + ssopBak1 = locator.getWebServiceSoap();
  164 + }
143 165 } catch (Exception e) {
144 166 e.printStackTrace();
145 167 }
... ... @@ -153,31 +175,20 @@ public class TrafficManageServiceImpl implements TrafficManageService{
153 175 // 数字格式化
154 176 DecimalFormat format = new DecimalFormat("0.00");
155 177  
156   - // 用户名
157   - private final String userNameOther = "user";
158   - // 密码
159   - private final String passwordOther = "user";
  178 + private final String userNameOther = SystemParamCache.getDrSysCityInterfaceUser();
  179 +
  180 + private final String passwordOther = SystemParamCache.getDrSysCityInterfacePassword();
  181 +
  182 + private final String userNameUp = SystemParamCache.getDrSysCityInterfaceBakUser();
  183 +
  184 + private final String passwordUp = SystemParamCache.getDrSysCityInterfaceBakPassword();
160 185  
161   - // 用户名
162   - private final String userNameUp = "user";
163   - // 密码
164   - private final String passwordUp = "user";
165 186 // 接收邮件人
166   - private final String emailSendToAddress = "113252620@qq.com";
  187 + private String[] emails = SystemParamCache.getMailWaybill().split(",");
  188 +
167 189 // 记录路单上线的成功、失败线路数
168 190 private Integer countSuccess,countFailure;
169 191  
170   - private synchronized com.bsth.webService.trafficManage.up.org.tempuri.WebServiceSoap getWebServiceSoapUp(){
171   - try {
172   - if(webServiceSoapUp == null){
173   - webServiceSoapUp = new com.bsth.webService.trafficManage.up.org.tempuri.WebServiceLocator().getWebServiceSoap();
174   - }
175   - }catch (Exception e){
176   - e.printStackTrace();
177   - }finally {
178   - return webServiceSoapUp;
179   - }
180   - }
181 192 /**
182 193 * 上传线路信息
183 194 */
... ... @@ -218,13 +229,18 @@ public class TrafficManageServiceImpl implements TrafficManageService{
218 229 }
219 230 String state;// 是否上传成功
220 231 // 调用上传方法
221   - if(getWebServiceSoapUp().setXL(userNameUp,passwordUp,xml).isSuccess()){
  232 + if(ssop1.setXL(userNameOther,passwordOther,xml).isSuccess()){
222 233 result = "success";
223 234 state = "1";
224 235 }else{
225 236 result = "failure";
226 237 state = "0";
227 238 }
  239 + if (ssopBak1.setXL(userNameOther,passwordOther,xml).isSuccess()) {
  240 + result += ",success";
  241 + } else {
  242 + result += ",failure";
  243 + }
228 244 logger.info("setXL:"+xml);
229 245 logger.info("setXL:"+result);
230 246 // 保存运管处上传记录
... ... @@ -319,6 +335,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
319 335 if(ssop.setCL(userNameOther, passwordOther, sBuffer.toString()).isSuccess()){
320 336 result = "success";
321 337 }
  338 + if (ssopBak != null && ssopBak.setCL(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  339 + result += ",success";
  340 + }
322 341 } catch (Exception e) {
323 342 logger.error("setCL:",e);
324 343 e.printStackTrace();
... ... @@ -356,7 +375,10 @@ public class TrafficManageServiceImpl implements TrafficManageService{
356 375 sBuffer.append("</SJs>");
357 376 if(ssop.setSJ(userNameOther, passwordOther, sBuffer.toString()).isSuccess()){
358 377 result = "success";
359   - };
  378 + }
  379 + if (ssopBak != null && ssopBak.setSJ(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  380 + result += ",success";
  381 + }
360 382 } catch (Exception e) {
361 383 logger.error("setSJ:",e);
362 384 e.printStackTrace();
... ... @@ -395,6 +417,7 @@ public class TrafficManageServiceImpl implements TrafficManageService{
395 417 String str = "", xlbm = null, oldXlbm = null;
396 418 List<Map<String,Object>> listGroup = null;
397 419 int scount = 0, ccount = 0;
  420 + int scount1 = 0, ccount1 = 0;
398 421 long start = System.currentTimeMillis();
399 422 try {
400 423 // 计数器
... ... @@ -436,6 +459,7 @@ public class TrafficManageServiceImpl implements TrafficManageService{
436 459 Line line = code2line.get(lineCode);
437 460 if(line == null || line.getInUse() == null || line.getInUse() == 0){
438 461 ccount++;
  462 + ccount1++;
439 463 continue;
440 464 }
441 465 if(counter % per == 0){
... ... @@ -512,7 +536,8 @@ public class TrafficManageServiceImpl implements TrafficManageService{
512 536 sf.append("</DLDS>");
513 537 // 去掉'号
514 538 str = sf.toString().replace("'","");
515   - scount += invokeSetLD(str, counter);
  539 + scount += invokeSetLD(ssop, str, counter, "master");
  540 + scount1 += invokeSetLD(ssopBak, str, counter, "slave");
516 541 counter = 0;
517 542 }
518 543 }
... ... @@ -521,18 +546,23 @@ public class TrafficManageServiceImpl implements TrafficManageService{
521 546 sf.append("</DLDS>");
522 547 // 去掉'号
523 548 str = sf.toString().replace("'","");
524   - scount += invokeSetLD(str, counter);
  549 + scount += invokeSetLD(ssop, str, counter, "master");
  550 + if (ssopBak != null) {
  551 + scount1 += invokeSetLD(ssopBak, str, counter, "slave");
  552 + }
525 553 }
526 554 } catch (Exception e) {
527 555 e.printStackTrace();
528 556 logger.error("setLD:", e);
529 557 } finally {
530 558 try {
531   - //发送邮件
532   - EmailBean mail = new EmailBean();
533   - mail.setSubject(IpUtils.getLocalIpAddress() +":路单日志数据"+date);
534   - mail.setContent("总数:" + (listGroup == null ? 0 : listGroup.size()) + "<br/>成功数:" + scount + "<br/>跳过数:" + ccount + "<br/>耗时:" + (System.currentTimeMillis() - start));
535   - sendEmailController.sendMail(emailSendToAddress, mail);
  559 + for (String email : emails) {
  560 + //发送邮件
  561 + EmailBean mail = new EmailBean();
  562 + mail.setSubject(IpUtils.getLocalIpAddress() +":路单日志数据"+date);
  563 + mail.setContent("总数:" + (listGroup == null ? 0 : listGroup.size()) + "<br/>成功数:" + scount + "," + scount1 + "<br/>跳过数:" + ccount + "," + ccount1 + "<br/>耗时:" + (System.currentTimeMillis() - start));
  564 + sendEmailController.sendMail(email, mail);
  565 + }
536 566 logger.info("setLD-sendMail:邮件发送成功!");
537 567 } catch (Exception e) {
538 568 e.printStackTrace();
... ... @@ -548,21 +578,21 @@ public class TrafficManageServiceImpl implements TrafficManageService{
548 578 * @param counter 分组数
549 579 * @return 成功返回counter 失败返回0
550 580 */
551   - private int invokeSetLD(String xml, int counter) {
  581 + private int invokeSetLD(WebServiceSoap ssop, String xml, int counter, String flag) {
552 582 for (int i = 0;i < 10;i++) {
553 583 try {
554 584 Results results = ssop.setLD(userNameOther, passwordOther, StringEscapeUtils.unescapeHtml(xml));
555 585 if (results.isSuccess()) {
556   - logger.info("setLD: " + xml);
  586 + logger.info("{} setLD: {}", flag, xml);
557 587 logger.info("setLD: 成功");
558 588 return counter;
559 589 } else if (i == 9) {
560   - logger.error("setLD: " + xml);
  590 + logger.error("{} setLD: {}", flag, xml);
561 591 logger.error("setLD: 失败," + results.getMessage());
562 592 }
563 593 Thread.sleep(2000);
564 594 } catch (Exception e) {
565   - logger.error("运管处接口调用异常", e);
  595 + logger.error("{} 运管处接口调用异常", flag, e);
566 596 try {
567 597 Thread.sleep(2000);
568 598 } catch (InterruptedException ex) {
... ... @@ -586,6 +616,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
586 616 if(rss.isSuccess()){
587 617 result = "success";
588 618 }
  619 + if (ssopBak != null && ssopBak.setLD(userNameOther, passwordOther, StringEscapeUtils.unescapeHtml(tmp)).isSuccess()) {
  620 + result += ",success";
  621 + }
589 622 } catch (Exception e) {
590 623 logger.error("setLD:",e);
591 624 e.printStackTrace();
... ... @@ -688,6 +721,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
688 721 if(ssop.setLCYH(userNameOther, passwordOther, sf.toString()).isSuccess()){
689 722 result = "success";
690 723 }
  724 + if (ssopBak != null && ssopBak.setLCYH(userNameOther, passwordOther, sf.toString()).isSuccess()) {
  725 + result += ",success";
  726 + }
691 727 } catch (Exception e) {
692 728 logger.error("setLCYH:",e);
693 729 e.printStackTrace();
... ... @@ -792,6 +828,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
792 828 if(ssop.setDDRB(userNameOther, passwordOther, sf.toString()).isSuccess()){
793 829 result = "success";
794 830 }
  831 + if (ssopBak != null && ssopBak.setDDRB(userNameOther, passwordOther, sf.toString()).isSuccess()) {
  832 + result += ",success";
  833 + }
795 834 } catch (Exception e) {
796 835 logger.error("setDDRB:",e);
797 836 e.printStackTrace();
... ... @@ -910,6 +949,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
910 949 if(ssop.setJHBC(userNameOther, passwordOther, sBuffer.toString()).isSuccess()){
911 950 result = "success";
912 951 }
  952 + if (ssopBak != null && ssopBak.setJHBC(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  953 + result += ",success";
  954 + }
913 955 } catch (Exception e) {
914 956 logger.error("setJHBC:",e);
915 957 e.printStackTrace();
... ... @@ -1103,14 +1145,20 @@ public class TrafficManageServiceImpl implements TrafficManageService{
1103 1145 result = "上传失败";
1104 1146 state = "0";
1105 1147 }
  1148 + if (ssopBak != null) {
  1149 + if (ssopBak.setSKB(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  1150 + result += ",上传成功";
  1151 + } else {
  1152 + result += ",上传失败";
  1153 + }
  1154 + }
  1155 + logger.info("xml:" + sBuffer.toString());
  1156 + logger.info("上传结果:" + result);
1106 1157 // 保存运管处上传记录
1107 1158 saveYgcUploadLog(ttinfoList,sBuffer.toString(),TrafficManageServiceImpl.UPLOAD_TYPE_SKB,state);
1108 1159 } catch (Exception e) {
1109 1160 logger.error("setSKB:", e);
1110 1161 e.printStackTrace();
1111   - }finally{
1112   - logger.info("setSKB:"+sBuffer.toString());
1113   - logger.info("setSKB:"+result);
1114 1162 }
1115 1163 return result;
1116 1164 }
... ... @@ -1191,6 +1239,7 @@ public class TrafficManageServiceImpl implements TrafficManageService{
1191 1239 }
1192 1240 }
1193 1241 }
  1242 +
1194 1243 /**
1195 1244 * 上传线路人员车辆配置信息
1196 1245 */
... ... @@ -1243,6 +1292,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
1243 1292 if(ssop.setXLPC(userNameOther, passwordOther, sBuffer.toString()).isSuccess()){
1244 1293 result = "success";
1245 1294 }
  1295 + if (ssopBak != null && ssopBak.setXLPC(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  1296 + result += ",success";
  1297 + }
1246 1298 } catch (Exception e) {
1247 1299 logger.error("setXLPC:",e);
1248 1300 e.printStackTrace();
... ... @@ -1303,6 +1355,9 @@ public class TrafficManageServiceImpl implements TrafficManageService{
1303 1355 if(ssop.setCS(userNameOther, passwordOther, sBuffer.toString()).isSuccess()){
1304 1356 result = "success";
1305 1357 }
  1358 + if (ssopBak != null && ssopBak.setCS(userNameOther, passwordOther, sBuffer.toString()).isSuccess()) {
  1359 + result += ",success";
  1360 + }
1306 1361 } catch (Exception e) {
1307 1362 logger.error("setCS:",e);
1308 1363 e.printStackTrace();
... ...
src/main/resources/static/pages/base/stationroute/add_stationroute_step2.html
... ... @@ -311,7 +311,7 @@ $(&#39;#add_stationroute_step2_modal&#39;).on(&#39;modal.show&#39;, function(event){
311 311 params.bufferPolygonWkt = params.bufferPolygonWkt ? 'POLYGON((' + params.bufferPolygonWkt + '))' : '';
312 312 params.shapedType = params.shapedType == '圆形' ? 'r' : 'd';
313 313 if (params.stationRouteCode == '请选择...' && params.stationMark == 'B') {
314   - params.stationRouteCode = '100';
  314 + params.stationRouteCode = 1;
315 315 } else {
316 316 params.stationRouteCode = parseInt(params.stationRouteCode) + 1;
317 317 }
... ...
src/main/resources/static/pages/base/stationroute/addstationstemplate.html
... ... @@ -201,7 +201,7 @@ $(&#39;#add_station_template_modal&#39;).on(&#39;modal.show&#39;, function(event) {
201 201 }
202 202  
203 203 function formatRoutes(params) {
204   - var stationRoutes = [], sectionRoutes = [], sectionRouteCode = 100;
  204 + var stationRoutes = [], sectionRoutes = [], sectionRouteCode = 1;
205 205 params.stationJSON.forEach(function(item) {
206 206 stationRoutes.push({stationName: item.name, shapedType: 'r', radius: 80, destroy: 0, distances: 0, toTime: 0, station: {stationName: item.name, centerPointWkt: 'POINT(' + item.potion.lng + ' ' + item.potion.lat + ')'}});
207 207 })
... ...
src/main/resources/static/pages/base/stationroute/edit_sectionroute.html
... ... @@ -138,7 +138,7 @@ $(&#39;#edit_sectionroute_modal&#39;).on(&#39;modal.show&#39;, function(e, polyline){
138 138 params.sectionTime = 0;
139 139  
140 140 if(params.sectionrouteCode == '请选择...') {
141   - params.sectionrouteCode = 100;
  141 + params.sectionrouteCode = 1;
142 142 } else {
143 143 params.sectionrouteCode = parseInt(params.sectionrouteCode) + 1;
144 144 }
... ...
src/main/resources/static/pages/base/stationroute/edit_stationroute_step2.html
... ... @@ -174,6 +174,18 @@
174 174 </div>
175 175 </div>
176 176 </div>
  177 + <!-- 绕改道 -->
  178 + <div class="form-body">
  179 + <div class="form-group">
  180 + <label class="control-label col-md-3">
  181 + 绕改道&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:
  182 + </label>
  183 + <div class="col-md-6">
  184 + <select name="detourStationRoute" class="form-control" id="detourStationRouteSelect" style="width:100%"></select>
  185 + <span class="help-block"> *说明:绕改道指当前站点至指定站点中间发生了临时走向调整(站点不调整),超过15个路段需要分开配置。 </span>
  186 + </div>
  187 + </div>
  188 + </div>
177 189 </form>
178 190 </div>
179 191 <div class="modal-footer">
... ... @@ -257,7 +269,7 @@ $(&#39;#edit_stationroute_step2_modal&#39;).on(&#39;modal.show&#39;, function(event){
257 269 else if(params.shapedType=='多边形')
258 270 params.shapedType='d';
259 271 if(params.stationRouteCode == '' || params.stationMark === 'B')
260   - params.stationRouteCode = 100;
  272 + params.stationRouteCode = 1;
261 273 if (params.stationMark !== 'B') {
262 274 params.stationRouteCode = parseInt(stationRouteCode.split('-')[0]) + 1;
263 275 }
... ... @@ -295,14 +307,20 @@ $(&#39;#edit_stationroute_step2_modal&#39;).on(&#39;modal.show&#39;, function(event){
295 307 var obj = {'id':'-1','text':'请选择...'};
296 308  
297 309 RoutesService.getzdlyInfo(p, function(array) {
  310 + debugger
298 311 // 定义路段路由长度、渲染拼音检索下拉框格式数据.
299   - var len_ = array.length, paramsD = new Array();
  312 + var len_ = array.length, paramsD = new Array(), stationRoutes = new Array();
  313 + const choose = {'id':'-1','text':'请选择...'};
300 314 if(len_>0) {
301   - paramsD.push({'id':'-1','text':'请选择...'});
  315 + paramsD.push(choose);
  316 + stationRoutes.push(choose)
302 317 // 遍历.
303 318 $.each(array, function(i, g){
304 319 if(g.stationName) {
305 320 if (g.stationRouteCode != editStationRoute.stationRouteCode) {
  321 + if (stationRoutes.length == 1 && g.stationRouteCode > editStationRoute.stationRouteCode) {
  322 + stationRoutes.push({'id': g.id, 'text': g.stationName + '(' + g.stationRouteCode + ')'});
  323 + }
306 324 if (editStationRoute.stationMark == 'E' && i == (len_ - 2)){
307 325 obj = {'id':g.stationRouteCode + '_' + 'E' + '_' + g.directions, 'text':g.stationName + ' (' + g.stationRouteCode + ')' + ' --' + RoutesOperation.dirdmToName(g.directions)};
308 326 paramsD.push(obj);
... ... @@ -316,12 +334,15 @@ $(&#39;#edit_stationroute_step2_modal&#39;).on(&#39;modal.show&#39;, function(event){
316 334 }
317 335 });
318 336 $('#stationrouteSelect').empty();
  337 + $('#detourStationRouteSelect').empty();
319 338 // 初始化上一个路段拼音检索下拉框.
320 339 initPinYinSelect2($('#stationrouteSelect'),paramsD,function(selector) {
321   - $('#stationrouteSelect').val(prevObj.id).select2();
  340 + $('#stationrouteSelect').val(prevObj.id).trigger('change');
322 341 $('#stationMarkSelect').val(editStationRoute.stationMark);
323 342 });
324   -
  343 + initPinYinSelect2($('#detourStationRouteSelect'), stationRoutes, function(selector) {
  344 + $('#detourStationRouteSelect').val(editStationRoute.detourStationRoute).trigger('change');
  345 + });
325 346 }
326 347 });
327 348 }
... ...
src/main/resources/static/pages/base/stationroute/editsection_inout.html
... ... @@ -144,7 +144,7 @@ $(&#39;#edit_section_modal&#39;).on(&#39;modal.show&#39;, function(e, currentSection){
144 144 params.sectionTime = 0;
145 145 error.hide();
146 146 if(params.sectionrouteCode == '请选择...') {
147   - params.sectionrouteCode = 100;
  147 + params.sectionrouteCode = 1;
148 148 } else {
149 149 params.sectionrouteCode = parseInt(params.sectionrouteCode) + 1;
150 150 }
... ...
src/main/resources/static/pages/base/stationroute/js/routes-operation.js
... ... @@ -1325,7 +1325,7 @@ var RoutesOperation = (function () {
1325 1325 sectionRoute['line.id'] = lineId;
1326 1326 sectionRoute.sectionCode = sectionCode;
1327 1327 sectionRoute.roadCoding = '';
1328   - sectionRoute.sectionrouteCode = 100;
  1328 + sectionRoute.sectionrouteCode = 1;
1329 1329 sectionRoute.sectionTime = 0;
1330 1330 sectionRoute.sectionDistance = 0;
1331 1331 sectionRoute.speedLimit = 60;
... ...
src/main/resources/static/pages/demo.html 0 → 100644
  1 +<!DOCTYPE html>
  2 +<html lang="zh-CN">
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6 + <title>运能运力一盘棋</title>
  7 + <script src="https://cdn.tailwindcss.com"></script>
  8 + <script src="//api.map.baidu.com/api?v=2.0&ak=1TgEKvYqohJyeGXnN6yHSSTb4psOarQw"></script>
  9 + <style>
  10 + .BMap_cpyCtrl {
  11 + display: none;
  12 + }
  13 +
  14 + .anchorBL {
  15 + display: none;
  16 + }
  17 +
  18 + ::-webkit-scrollbar {
  19 + width: 6px;
  20 + height: 6px;
  21 + }
  22 +
  23 + ::-webkit-scrollbar-track {
  24 + background: #f1f1f1;
  25 + border-radius: 3px;
  26 + }
  27 +
  28 + ::-webkit-scrollbar-thumb {
  29 + background: #c1c1c1;
  30 + border-radius: 3px;
  31 + }
  32 +
  33 + ::-webkit-scrollbar-thumb:hover {
  34 + background: #a8a8a8;
  35 + }
  36 +
  37 + * {
  38 + -ms-overflow-style: -ms-autohiding-scrollbar;
  39 + scrollbar-width: thin;
  40 + scrollbar-color: #c1c1c1 #f1f1f1;
  41 + }
  42 +
  43 + .condition-panel {
  44 + max-height: 0;
  45 + overflow: hidden;
  46 + transition: max-height 0.3s ease-out;
  47 + }
  48 +
  49 + .condition-panel.open {
  50 + max-height: 500px;
  51 + }
  52 +
  53 + .search-tag {
  54 + display: inline-flex;
  55 + align-items: center;
  56 + gap: 4px;
  57 + padding: 2px 8px;
  58 + background: #1e6bb8;
  59 + color: white;
  60 + border-radius: 4px;
  61 + font-size: 12px;
  62 + margin: 2px;
  63 + }
  64 +
  65 + .search-tag .remove-tag {
  66 + cursor: pointer;
  67 + font-size: 14px;
  68 + line-height: 1;
  69 + }
  70 +
  71 + .search-tag .remove-tag:hover {
  72 + opacity: 0.8;
  73 + }
  74 +
  75 + .line-search-dropdown {
  76 + position: absolute;
  77 + top: 100%;
  78 + left: 0;
  79 + right: 0;
  80 + background: white;
  81 + border: 1px solid #ddd;
  82 + border-radius: 4px;
  83 + max-height: 150px;
  84 + overflow-y: auto;
  85 + z-index: 100;
  86 + display: none;
  87 + }
  88 +
  89 + .line-search-dropdown.show {
  90 + display: block;
  91 + }
  92 +
  93 + .line-option {
  94 + padding: 8px 12px;
  95 + cursor: pointer;
  96 + font-size: 13px;
  97 + display: flex;
  98 + align-items: center;
  99 + }
  100 +
  101 + .line-option:hover {
  102 + background: #f0f7ff;
  103 + }
  104 +
  105 + .location-marker {
  106 + cursor: crosshair;
  107 + }
  108 + </style>
  109 +</head>
  110 +<body class="m-0 p-0 box-border w-full h-full overflow-hidden font-['Microsoft_YaHei',Arial,sans-serif]">
  111 + <div id="map" class="w-full h-full absolute top-0 left-0 z-10"></div>
  112 +
  113 + <div class="absolute top-0 left-0 right-0 h-[50px] bg-[rgba(30,107,184,0.9)] text-white flex items-center justify-center text-[18px] font-bold z-[100] shadow-[0_2px_4px_rgba(0,0,0,0.1)]">
  114 + 运能运力一盘棋
  115 + </div>
  116 +
  117 + <div class="absolute top-[60px] left-[20px] w-[400px] h-[30vh] bg-[rgba(255,255,255,0.95)] rounded-lg shadow-[0_2px_10px_rgba(0,0,0,0.15)] overflow-hidden z-[60]">
  118 + <div class="px-[15px] py-3 bg-[rgba(233,245,255,0.95)] border-b border-[#ddd] text-[14px] font-bold text-[#1e6bb8] text-center">
  119 + 车辆运营概览
  120 + </div>
  121 + <div class="p-4 grid grid-cols-2 gap-5">
  122 + <div class="flex items-start">
  123 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  124 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  125 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M18.92 6.01C18.72 5.42 18.16 5 17.5 5H6.5c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  126 + </div>
  127 + </div>
  128 + <div class="flex flex-col ml-[15px]">
  129 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">总车辆数</div>
  130 + <div class="text-[18px] font-bold text-[#1e6bb8]">500<span class="text-[12px] text-[#999] font-normal">辆</span></div>
  131 + </div>
  132 + </div>
  133 + <div class="flex items-start">
  134 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  135 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  136 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  137 + </div>
  138 + </div>
  139 + <div class="flex flex-col ml-[15px]">
  140 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当前运营车辆</div>
  141 + <div class="text-[18px] font-bold text-[#1e6bb8]">200<span class="text-[12px] text-[#999] font-normal">辆</span></div>
  142 + </div>
  143 + </div>
  144 + <div class="flex items-start">
  145 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  146 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  147 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  148 + </div>
  149 + </div>
  150 + <div class="flex flex-col ml-[15px]">
  151 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">今日计划出车</div>
  152 + <div class="text-[18px] font-bold text-[#1e6bb8]">300<span class="text-[12px] text-[#999] font-normal">辆</span></div>
  153 + </div>
  154 + </div>
  155 + <div class="flex items-start">
  156 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  157 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  158 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  159 + </div>
  160 + </div>
  161 + <div class="flex flex-col ml-[15px]">
  162 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">今日已出车</div>
  163 + <div class="text-[18px] font-bold text-[#1e6bb8]">210<span class="text-[12px] text-[#999] font-normal">辆</span></div>
  164 + </div>
  165 + </div>
  166 + <div class="flex items-start">
  167 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  168 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  169 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M19.79 13.42c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39l-1.43-1.43c.64-.66.99-1.53.99-2.54 0-.55-.1-1.07-.29-1.56zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  170 + </div>
  171 + </div>
  172 + <div class="flex flex-col ml-[15px]">
  173 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当前维保车辆</div>
  174 + <div class="text-[18px] font-bold text-[#1e6bb8]">5<span class="text-[12px] text-[#999] font-normal">辆</span></div>
  175 + </div>
  176 + </div>
  177 + <div class="flex items-start">
  178 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  179 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  180 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M19.43 12.98c.04-.3.07-.61.07-.98 0-.37-.03-.74-.07-1.1l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.36-.07.73-.07 1.1 0 .37.03.74.07 1.1l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  181 + </div>
  182 + </div>
  183 + <div class="flex flex-col ml-[15px]">
  184 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当前空驶率</div>
  185 + <div class="text-[18px] font-bold text-[#1e6bb8]">15<span class="text-[12px] text-[#999] font-normal">%</span></div>
  186 + </div>
  187 + </div>
  188 + </div>
  189 + </div>
  190 +
  191 + <div class="absolute top-[calc(60px+30vh+20px)] left-[20px] w-[400px] h-[30vh] bg-[rgba(255,255,255,0.95)] rounded-lg shadow-[0_2px_10px_rgba(0,0,0,0.15)] overflow-hidden z-[60]">
  192 + <div class="px-[15px] py-3 bg-[rgba(233,245,255,0.95)] border-b border-[#ddd] text-[14px] font-bold text-[#1e6bb8] text-center">
  193 + 人员运营概览
  194 + </div>
  195 + <div class="p-4 grid grid-cols-2 gap-5">
  196 + <div class="flex items-start">
  197 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  198 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  199 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  200 + </div>
  201 + </div>
  202 + <div class="flex flex-col ml-[15px]">
  203 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">总人员数</div>
  204 + <div class="text-[18px] font-bold text-[#1e6bb8]">600<span class="text-[12px] text-[#999] font-normal">人</span></div>
  205 + </div>
  206 + </div>
  207 + <div class="flex items-start">
  208 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  209 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  210 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  211 + </div>
  212 + </div>
  213 + <div class="flex flex-col ml-[15px]">
  214 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当前运营人员</div>
  215 + <div class="text-[18px] font-bold text-[#1e6bb8]">200<span class="text-[12px] text-[#999] font-normal">人</span></div>
  216 + </div>
  217 + </div>
  218 + <div class="flex items-start">
  219 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  220 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  221 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  222 + </div>
  223 + </div>
  224 + <div class="flex flex-col ml-[15px]">
  225 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">今日计划出人</div>
  226 + <div class="text-[18px] font-bold text-[#1e6bb8]">350<span class="text-[12px] text-[#999] font-normal">人</span></div>
  227 + </div>
  228 + </div>
  229 + <div class="flex items-start">
  230 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  231 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  232 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  233 + </div>
  234 + </div>
  235 + <div class="flex flex-col ml-[15px]">
  236 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">今日已出人</div>
  237 + <div class="text-[18px] font-bold text-[#1e6bb8]">230<span class="text-[12px] text-[#999] font-normal">人</span></div>
  238 + </div>
  239 + </div>
  240 + <div class="flex items-start">
  241 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  242 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  243 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  244 + </div>
  245 + </div>
  246 + <div class="flex flex-col ml-[15px]">
  247 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当前病休人员</div>
  248 + <div class="text-[18px] font-bold text-[#1e6bb8]">1<span class="text-[12px] text-[#999] font-normal">人</span></div>
  249 + </div>
  250 + </div>
  251 + <div class="flex items-start">
  252 + <div class="relative w-[60px] h-[60px] flex flex-col items-center">
  253 + <div class="w-[50px] h-[50px] bg-[#1e6bb8] rounded-full opacity-80 flex items-center justify-center">
  254 + <div class="w-[30px] h-[30px] bg-white rounded-full flex items-center justify-center shadow-[0_2px_4px_rgba(0,0,0,0.1)]" style="background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22%231e6bb8%22%3E%3Cpath d=%22M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z%22/%3E%3C/svg%3E'); background-size: 60%; background-position: center; background-repeat: no-repeat;"></div>
  255 + </div>
  256 + </div>
  257 + <div class="flex flex-col ml-[15px]">
  258 + <div class="text-[12px] text-[#666] mb-1 whitespace-nowrap">当日已完成运营</div>
  259 + <div class="text-[18px] font-bold text-[#1e6bb8]">30<span class="text-[12px] text-[#999] font-normal">人</span></div>
  260 + </div>
  261 + </div>
  262 + </div>
  263 + </div>
  264 +
  265 + <div id="search-panel-container" class="absolute top-[60px] left-[calc(400px+40px)] right-[20px] z-[60]">
  266 + <div id="search-condition-panel" class="bg-[rgba(255,255,255,0.95)] rounded-lg shadow-[0_2px_10px_rgba(0,0,0,0.15)] p-4 mb-4">
  267 + <div class="flex gap-2 mb-4">
  268 + <div class="flex-1 relative">
  269 + <div id="search-input" class="min-h-[40px] px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8] cursor-text flex flex-wrap items-center gap-1">
  270 + <span id="search-tags"></span>
  271 + <input type="text" id="search-text" class="flex-1 min-w-[100px] outline-none border-none text-[13px]" placeholder="点击选择搜索条件...">
  272 + </div>
  273 + </div>
  274 + <button id="btn-search" class="bg-[#1e6bb8] text-white px-6 py-2 rounded text-[13px] font-medium hover:bg-[#155a96] transition-colors">
  275 + 搜索
  276 + </button>
  277 + </div>
  278 + <div id="toggle-arrow" class="flex justify-center items-center h-6 cursor-pointer mb-2">
  279 + <svg id="arrow-icon" class="w-5 h-5 text-gray-500 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  280 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
  281 + </svg>
  282 + </div>
  283 + <div id="condition-panel" class="condition-panel">
  284 + <div class="grid grid-cols-2 gap-4 mb-4">
  285 + <div>
  286 + <label class="block text-[12px] text-[#666] mb-1">任务类型</label>
  287 + <div class="flex gap-2">
  288 + <label class="flex items-center cursor-pointer">
  289 + <input type="radio" name="dispatchType" value="urgent" class="mr-1" data-type="dispatchType" data-label="紧急" data-value="urgent">
  290 + <span class="text-[13px]">紧急</span>
  291 + </label>
  292 + <label class="flex items-center cursor-pointer">
  293 + <input type="radio" name="dispatchType" value="normal" class="mr-1" data-type="dispatchType" data-label="普通" data-value="normal">
  294 + <span class="text-[13px]">普通</span>
  295 + </label>
  296 + </div>
  297 + </div>
  298 + <div>
  299 + <label class="block text-[12px] text-[#666] mb-1">线路选择</label>
  300 + <div class="relative">
  301 + <div id="lineSelectContainer" class="min-h-[40px] px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8] cursor-pointer flex flex-wrap items-center gap-1">
  302 + <span id="lineSelectedTags"></span>
  303 + <input type="text" id="lineSearchInput" class="flex-1 min-w-[100px] outline-none border-none text-[13px]" placeholder="搜索线路...">
  304 + </div>
  305 + <div id="lineDropdown" class="line-search-dropdown">
  306 + <div class="line-option" data-value="803111" data-label="线路1">
  307 + <input type="checkbox" class="line-checkbox mr-2" data-value="803111" data-label="线路1">
  308 + <span>803111 - 线路1</span>
  309 + </div>
  310 + <div class="line-option" data-value="803112" data-label="线路2">
  311 + <input type="checkbox" class="line-checkbox mr-2" data-value="803112" data-label="线路2">
  312 + <span>803112 - 线路2</span>
  313 + </div>
  314 + <div class="line-option" data-value="803113" data-label="线路3">
  315 + <input type="checkbox" class="line-checkbox mr-2" data-value="803113" data-label="线路3">
  316 + <span>803113 - 线路3</span>
  317 + </div>
  318 + <div class="line-option" data-value="803114" data-label="线路4">
  319 + <input type="checkbox" class="line-checkbox mr-2" data-value="803114" data-label="线路4">
  320 + <span>803114 - 线路4</span>
  321 + </div>
  322 + <div class="line-option" data-value="803115" data-label="线路5">
  323 + <input type="checkbox" class="line-checkbox mr-2" data-value="803115" data-label="线路5">
  324 + <span>803115 - 线路5</span>
  325 + </div>
  326 + </div>
  327 + </div>
  328 + </div>
  329 + </div>
  330 +
  331 + <div class="grid grid-cols-2 gap-4 mb-4">
  332 + <div>
  333 + <label class="block text-[12px] text-[#666] mb-1">停运时长</label>
  334 + <select id="stopDuration" class="w-full px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8]" data-type="stopDuration">
  335 + <option value="">请选择</option>
  336 + <option value="30" data-label="30分钟">30分钟</option>
  337 + <option value="60" data-label="1小时">1小时</option>
  338 + <option value="120" data-label="2小时">2小时</option>
  339 + </select>
  340 + </div>
  341 + <div>
  342 + <label class="block text-[12px] text-[#666] mb-1">任务位置 <span id="locationHint" class="text-[#999] text-[11px]">(右键地图设置)</span></label>
  343 + <div class="flex gap-2">
  344 + <input type="text" id="taskLocation" class="flex-1 px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8]" readonly placeholder="未设置">
  345 + <button id="clearLocation" class="px-3 py-2 bg-gray-200 text-gray-700 rounded text-[13px] hover:bg-gray-300 transition-colors">
  346 + 清除
  347 + </button>
  348 + </div>
  349 + </div>
  350 + </div>
  351 +
  352 + <div class="grid grid-cols-2 gap-4 mb-4">
  353 + <div>
  354 + <label class="block text-[12px] text-[#666] mb-1">开始时间</label>
  355 + <input type="datetime-local" id="startTime" class="w-full px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8]" data-type="startTime">
  356 + </div>
  357 + <div>
  358 + <label class="block text-[12px] text-[#666] mb-1">结束时间</label>
  359 + <input type="datetime-local" id="endTime" class="w-full px-3 py-2 border border-[#ddd] rounded text-[13px] focus:outline-none focus:border-[#1e6bb8]" data-type="endTime">
  360 + </div>
  361 + </div>
  362 + </div>
  363 + </div>
  364 +
  365 + <div id="results-panel" class="bg-[rgba(255,255,255,0.95)] rounded-lg shadow-[0_2px_10px_rgba(0,0,0,0.15)] p-4 hidden">
  366 + <div class="flex justify-between items-center mb-3">
  367 + <div class="text-[14px] font-bold text-[#1e6bb8]">搜索结果</div>
  368 + <button id="close-results" class="text-gray-500 hover:text-gray-700 p-1">
  369 + <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  370 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
  371 + </svg>
  372 + </button>
  373 + </div>
  374 + <div class="max-h-[300px] overflow-y-auto">
  375 + <table class="w-full text-[12px]">
  376 + <thead class="bg-[#f5f8fc] sticky top-0">
  377 + <tr>
  378 + <th class="px-2 py-2 text-left font-medium text-[#666]">序号</th>
  379 + <th class="px-2 py-2 text-left font-medium text-[#666]">车辆编码</th>
  380 + <th class="px-2 py-2 text-left font-medium text-[#666]">设备编码</th>
  381 + <th class="px-2 py-2 text-left font-medium text-[#666]">线路</th>
  382 + <th class="px-2 py-2 text-left font-medium text-[#666]">上下行</th>
  383 + </tr>
  384 + </thead>
  385 + <tbody id="results-tbody">
  386 + </tbody>
  387 + </table>
  388 + </div>
  389 + </div>
  390 + </div>
  391 +
  392 + <script>
  393 + document.addEventListener('DOMContentLoaded', function() {
  394 + var map = new BMap.Map("map");
  395 + var point = new BMap.Point(121.0, 31.2);
  396 + map.centerAndZoom(point, 8);
  397 + map.enableScrollWheelZoom(true);
  398 +
  399 + var searchConditions = {};
  400 + var locationMarker = null;
  401 + var locationCircle = null;
  402 + var taskLocation = null;
  403 + var selectedLines = {};
  404 +
  405 + var vehicleLocations = [
  406 + {lng: 120.7, lat: 31.1, name: "W105-01", status: "正常"},
  407 + {lng: 120.8, lat: 31.3, name: "W105-02", status: "正常"},
  408 + {lng: 120.9, lat: 31.2, name: "W107-01", status: "正常"},
  409 + {lng: 121.1, lat: 31.4, name: "W107-02", status: "正常"},
  410 + {lng: 121.2, lat: 31.1, name: "W108-01", status: "正常"},
  411 + {lng: 121.0, lat: 31.3, name: "W108-02", status: "正常"}
  412 + ];
  413 +
  414 + vehicleLocations.forEach(function(vehicle) {
  415 + var point = new BMap.Point(vehicle.lng, vehicle.lat);
  416 + var fillColor = vehicle.status === "正常" ? "%231e6bb8" : "%23e67e22";
  417 + var icon = new BMap.Icon("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='14' fill='" + fillColor + "' stroke='white' stroke-width='2'/%3E%3Ccircle cx='16' cy='16' r='6' fill='white' fill-opacity='0.3'/%3E%3C/svg%3E", new BMap.Size(32, 32));
  418 + var marker = new BMap.Marker(point, {icon: icon});
  419 + var infoWindow = new BMap.InfoWindow("<div style='padding:10px;'><strong>车辆编号:</strong> " + vehicle.name + "<br/><strong>状态:</strong> " + vehicle.status + "</div>");
  420 + marker.addEventListener("click", function() {
  421 + this.openInfoWindow(infoWindow);
  422 + });
  423 + map.addOverlay(marker);
  424 + });
  425 +
  426 + /**
  427 + * 在地图上绘制路线
  428 + * @param {Array} points - 地图点数组
  429 + * @param {string} color - 路线颜色
  430 + */
  431 + function drawRoute(points, color) {
  432 + var polyline = new BMap.Polyline(points, {
  433 + strokeColor: color,
  434 + strokeWeight: 8,
  435 + strokeOpacity: 0.7,
  436 + strokeStyle: "solid"
  437 + });
  438 + map.addOverlay(polyline);
  439 + }
  440 +
  441 + var routes = [
  442 + [
  443 + new BMap.Point(120.5, 31.1),
  444 + new BMap.Point(120.7, 31.1),
  445 + new BMap.Point(120.9, 31.1),
  446 + new BMap.Point(121.1, 31.1),
  447 + new BMap.Point(121.3, 31.1),
  448 + new BMap.Point(121.5, 31.1)
  449 + ]
  450 + ];
  451 +
  452 + routes.forEach(function(route) {
  453 + drawRoute(route, "#1e6bb8");
  454 + });
  455 +
  456 + map.addControl(new BMap.NavigationControl({
  457 + type: BMAP_NAVIGATION_CONTROL_ZOOM,
  458 + anchor: BMAP_ANCHOR_TOP_LEFT,
  459 + offset: new BMap.Size(10, 70)
  460 + }));
  461 +
  462 + map.addControl(new BMap.ScaleControl({
  463 + anchor: BMAP_ANCHOR_BOTTOM_LEFT
  464 + }));
  465 +
  466 + var searchInput = document.getElementById('search-input');
  467 + var searchText = document.getElementById('search-text');
  468 + var conditionPanel = document.getElementById('condition-panel');
  469 + var searchTags = document.getElementById('search-tags');
  470 + var toggleArrow = document.getElementById('toggle-arrow');
  471 + var arrowIcon = document.getElementById('arrow-icon');
  472 +
  473 + /**
  474 + * 切换条件面板展开/收拢状态
  475 + */
  476 + function toggleConditionPanel() {
  477 + if (conditionPanel.classList.contains('open')) {
  478 + conditionPanel.classList.remove('open');
  479 + arrowIcon.style.transform = 'rotate(0deg)';
  480 + } else {
  481 + conditionPanel.classList.add('open');
  482 + arrowIcon.style.transform = 'rotate(180deg)';
  483 + }
  484 + }
  485 +
  486 + toggleArrow.addEventListener('click', function(e) {
  487 + e.stopPropagation();
  488 + toggleConditionPanel();
  489 + });
  490 +
  491 + searchInput.addEventListener('click', function(e) {
  492 + if (!conditionPanel.classList.contains('open')) {
  493 + toggleConditionPanel();
  494 + }
  495 + searchText.focus();
  496 + });
  497 +
  498 + document.querySelectorAll('input[type="radio"][data-type]').forEach(function(radio) {
  499 + radio.addEventListener('change', function() {
  500 + if (this.checked) {
  501 + var type = this.dataset.type;
  502 + var label = this.dataset.label;
  503 + var value = this.dataset.value;
  504 + addCondition(type, label, value);
  505 + }
  506 + });
  507 + });
  508 +
  509 + document.getElementById('stopDuration').addEventListener('change', function() {
  510 + var selectedOption = this.options[this.selectedIndex];
  511 + if (selectedOption.value) {
  512 + addCondition('stopDuration', selectedOption.dataset.label, selectedOption.value);
  513 + } else {
  514 + removeCondition('stopDuration');
  515 + }
  516 + });
  517 +
  518 + document.getElementById('startTime').addEventListener('change', function() {
  519 + if (this.value) {
  520 + addCondition('startTime', '开始:' + this.value, this.value);
  521 + } else {
  522 + removeCondition('startTime');
  523 + }
  524 + });
  525 +
  526 + document.getElementById('endTime').addEventListener('change', function() {
  527 + if (this.value) {
  528 + addCondition('endTime', '结束:' + this.value, this.value);
  529 + } else {
  530 + removeCondition('endTime');
  531 + }
  532 + });
  533 +
  534 + var lineSelectContainer = document.getElementById('lineSelectContainer');
  535 + var lineSearchInput = document.getElementById('lineSearchInput');
  536 + var lineDropdown = document.getElementById('lineDropdown');
  537 + var lineSelectedTags = document.getElementById('lineSelectedTags');
  538 +
  539 + /**
  540 + * 渲染已选择的线路标签
  541 + */
  542 + function renderLineTags() {
  543 + lineSelectedTags.innerHTML = '';
  544 + for (var value in selectedLines) {
  545 + var tag = document.createElement('span');
  546 + tag.className = 'search-tag';
  547 + tag.innerHTML = selectedLines[value] + '<span class="remove-tag" data-value="' + value + '">×</span>';
  548 + lineSelectedTags.appendChild(tag);
  549 + }
  550 +
  551 + document.querySelectorAll('#lineSelectedTags .remove-tag').forEach(function(btn) {
  552 + btn.addEventListener('click', function(e) {
  553 + e.stopPropagation();
  554 + var value = this.dataset.value;
  555 + removeLine(value);
  556 + });
  557 + });
  558 +
  559 + updateLineCondition();
  560 + }
  561 +
  562 + /**
  563 + * 添加线路选择
  564 + * @param {string} value - 线路值
  565 + * @param {string} label - 线路标签
  566 + */
  567 + function addLine(value, label) {
  568 + selectedLines[value] = label;
  569 + renderLineTags();
  570 + updateLineCheckboxes();
  571 + }
  572 +
  573 + /**
  574 + * 移除线路选择
  575 + * @param {string} value - 线路值
  576 + */
  577 + function removeLine(value) {
  578 + delete selectedLines[value];
  579 + renderLineTags();
  580 + updateLineCheckboxes();
  581 + }
  582 +
  583 + /**
  584 + * 更新搜索条件中的线路
  585 + */
  586 + function updateLineCondition() {
  587 + var values = Object.keys(selectedLines);
  588 + var labels = Object.values(selectedLines);
  589 + if (values.length > 0) {
  590 + searchConditions['line'] = { label: labels.join(','), value: values.join(',') };
  591 + } else {
  592 + delete searchConditions['line'];
  593 + }
  594 + renderTags();
  595 + }
  596 +
  597 + /**
  598 + * 更新下拉框中复选框的选中状态
  599 + */
  600 + function updateLineCheckboxes() {
  601 + document.querySelectorAll('.line-checkbox').forEach(function(checkbox) {
  602 + checkbox.checked = !!selectedLines[checkbox.dataset.value];
  603 + });
  604 + }
  605 +
  606 + lineSelectContainer.addEventListener('click', function(e) {
  607 + if (e.target.classList.contains('remove-tag')) {
  608 + return;
  609 + }
  610 + lineDropdown.classList.add('show');
  611 + lineSearchInput.focus();
  612 + });
  613 +
  614 + lineSearchInput.addEventListener('focus', function() {
  615 + lineDropdown.classList.add('show');
  616 + });
  617 +
  618 + lineSearchInput.addEventListener('input', function() {
  619 + var filter = this.value.toLowerCase();
  620 + var options = lineDropdown.querySelectorAll('.line-option');
  621 + options.forEach(function(option) {
  622 + var text = option.textContent.toLowerCase();
  623 + option.style.display = text.indexOf(filter) > -1 ? 'flex' : 'none';
  624 + });
  625 + });
  626 +
  627 + document.querySelectorAll('.line-checkbox').forEach(function(checkbox) {
  628 + checkbox.addEventListener('change', function(e) {
  629 + e.stopPropagation();
  630 + var value = this.dataset.value;
  631 + var label = this.dataset.label;
  632 + if (this.checked) {
  633 + addLine(value, label);
  634 + } else {
  635 + removeLine(value);
  636 + }
  637 + });
  638 + });
  639 +
  640 + document.querySelectorAll('.line-option').forEach(function(option) {
  641 + option.addEventListener('click', function(e) {
  642 + if (e.target.type === 'checkbox') {
  643 + return;
  644 + }
  645 + var checkbox = this.querySelector('.line-checkbox');
  646 + checkbox.checked = !checkbox.checked;
  647 + var value = checkbox.dataset.value;
  648 + var label = checkbox.dataset.label;
  649 + if (checkbox.checked) {
  650 + addLine(value, label);
  651 + } else {
  652 + removeLine(value);
  653 + }
  654 + });
  655 + });
  656 +
  657 + document.addEventListener('click', function(e) {
  658 + if (!lineSelectContainer.contains(e.target) && !lineDropdown.contains(e.target)) {
  659 + lineDropdown.classList.remove('show');
  660 + }
  661 + });
  662 +
  663 + map.addEventListener('rightclick', function(e) {
  664 + taskLocation = e.point;
  665 + var lng = e.point.lng.toFixed(4);
  666 + var lat = e.point.lat.toFixed(4);
  667 +
  668 + document.getElementById('taskLocation').value = lng + ', ' + lat;
  669 + addCondition('location', '位置:' + lng + ',' + lat, lng + ',' + lat);
  670 +
  671 + if (locationMarker) {
  672 + map.removeOverlay(locationMarker);
  673 + }
  674 + if (locationCircle) {
  675 + map.removeOverlay(locationCircle);
  676 + }
  677 +
  678 + var locationIcon = new BMap.Icon("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath d='M24 4C15.163 4 8 11.163 8 20c0 5.219 3.438 11.641 8.203 17.484a31.841 31.841 0 0 0 7.797 9.672 31.807 31.807 0 0 0 7.797-9.672C36.562 31.641 40 25.219 40 20c0-8.837-7.163-16-16-16zm0 24a8 8 0 1 1 0-16 8 8 0 0 1 0 16z' fill='%231e6bb8'/%3E%3Ccircle cx='24' cy='20' r='5' fill='white'/%3E%3C/svg%3E", new BMap.Size(48, 48));
  679 + locationIcon.setAnchor(new BMap.Size(24, 48));
  680 + locationMarker = new BMap.Marker(e.point, {icon: locationIcon});
  681 + map.addOverlay(locationMarker);
  682 + });
  683 +
  684 + document.getElementById('clearLocation').addEventListener('click', function() {
  685 + document.getElementById('taskLocation').value = '';
  686 + removeCondition('location');
  687 + if (locationMarker) {
  688 + map.removeOverlay(locationMarker);
  689 + locationMarker = null;
  690 + }
  691 + if (locationCircle) {
  692 + map.removeOverlay(locationCircle);
  693 + locationCircle = null;
  694 + }
  695 + taskLocation = null;
  696 + });
  697 +
  698 + /**
  699 + * 添加搜索条件
  700 + * @param {string} type - 条件类型
  701 + * @param {string} label - 条件显示标签
  702 + * @param {*} value - 条件值
  703 + */
  704 + function addCondition(type, label, value) {
  705 + removeCondition(type);
  706 + searchConditions[type] = { label: label, value: value };
  707 + renderTags();
  708 + }
  709 +
  710 + /**
  711 + * 移除搜索条件
  712 + * @param {string} type - 要移除的条件类型
  713 + */
  714 + function removeCondition(type) {
  715 + delete searchConditions[type];
  716 + renderTags();
  717 + }
  718 +
  719 + /**
  720 + * 渲染搜索标签
  721 + */
  722 + function renderTags() {
  723 + searchTags.innerHTML = '';
  724 + for (var type in searchConditions) {
  725 + var tag = document.createElement('span');
  726 + tag.className = 'search-tag';
  727 + tag.innerHTML = searchConditions[type].label + '<span class="remove-tag" data-type="' + type + '">×</span>';
  728 + searchTags.appendChild(tag);
  729 + }
  730 +
  731 + document.querySelectorAll('.remove-tag').forEach(function(btn) {
  732 + btn.addEventListener('click', function(e) {
  733 + e.stopPropagation();
  734 + var type = this.dataset.type;
  735 + removeCondition(type);
  736 +
  737 + if (type === 'dispatchType') {
  738 + document.querySelectorAll('input[name="dispatchType"]').forEach(function(r) { r.checked = false; });
  739 + } else if (type === 'stopDuration') {
  740 + document.getElementById('stopDuration').value = '';
  741 + } else if (type === 'startTime') {
  742 + document.getElementById('startTime').value = '';
  743 + } else if (type === 'endTime') {
  744 + document.getElementById('endTime').value = '';
  745 + } else if (type === 'location') {
  746 + document.getElementById('taskLocation').value = '';
  747 + if (locationMarker) {
  748 + map.removeOverlay(locationMarker);
  749 + locationMarker = null;
  750 + }
  751 + if (locationCircle) {
  752 + map.removeOverlay(locationCircle);
  753 + locationCircle = null;
  754 + }
  755 + taskLocation = null;
  756 + } else if (type === 'line') {
  757 + selectedLines = {};
  758 + renderLineTags();
  759 + updateLineCheckboxes();
  760 + }
  761 + });
  762 + });
  763 + }
  764 +
  765 + document.getElementById('btn-search').addEventListener('click', function() {
  766 + if (conditionPanel.classList.contains('open')) {
  767 + toggleConditionPanel();
  768 + }
  769 +
  770 + var mockData = {
  771 + "gpsList": [
  772 + {
  773 + "lineId": "803111",
  774 + "subLineId": 0,
  775 + "deviceId": "77L0K001",
  776 + "carparkNo": "",
  777 + "stopNo": "",
  778 + "lon": 0.0,
  779 + "lat": 0.0,
  780 + "timestamp": 1772591197000,
  781 + "serverTimestamp": 1772591194152,
  782 + "speed": 0.0,
  783 + "direction": 0.0,
  784 + "state": 0,
  785 + "upDown": 1,
  786 + "nbbm": "L0K-001",
  787 + "schId": 1067376191,
  788 + "version": 69,
  789 + "instation": 0,
  790 + "signalState": "normal",
  791 + "abnormalStatus": "outBounds",
  792 + "outOfBoundDistance": 1.3019440198864643E7,
  793 + "valid": 1,
  794 + "source": -1,
  795 + "offline": false,
  796 + "service": true
  797 + },
  798 + {
  799 + "lineId": "803112",
  800 + "subLineId": 0,
  801 + "deviceId": "77L0K002",
  802 + "carparkNo": "",
  803 + "stopNo": "",
  804 + "lon": 0.0,
  805 + "lat": 0.0,
  806 + "timestamp": 1772591197000,
  807 + "serverTimestamp": 1772591194152,
  808 + "speed": 0.0,
  809 + "direction": 0.0,
  810 + "state": 0,
  811 + "upDown": 0,
  812 + "nbbm": "L0K-002",
  813 + "schId": 1067376192,
  814 + "version": 70,
  815 + "instation": 0,
  816 + "signalState": "normal",
  817 + "abnormalStatus": "",
  818 + "outOfBoundDistance": 0,
  819 + "valid": 1,
  820 + "source": -1,
  821 + "offline": false,
  822 + "service": true
  823 + },
  824 + {
  825 + "lineId": "803113",
  826 + "subLineId": 0,
  827 + "deviceId": "77L0K003",
  828 + "carparkNo": "",
  829 + "stopNo": "",
  830 + "lon": 0.0,
  831 + "lat": 0.0,
  832 + "timestamp": 1772591197000,
  833 + "serverTimestamp": 1772591194152,
  834 + "speed": 0.0,
  835 + "direction": 0.0,
  836 + "state": 0,
  837 + "upDown": 1,
  838 + "nbbm": "L0K-003",
  839 + "schId": 1067376193,
  840 + "version": 71,
  841 + "instation": 0,
  842 + "signalState": "normal",
  843 + "abnormalStatus": "",
  844 + "outOfBoundDistance": 0,
  845 + "valid": 1,
  846 + "source": -1,
  847 + "offline": false,
  848 + "service": true
  849 + }
  850 + ],
  851 + "overspeedList": [],
  852 + "scis": []
  853 + };
  854 +
  855 + displayResults(mockData.gpsList);
  856 + });
  857 +
  858 + /**
  859 + * 关闭搜索结果面板
  860 + */
  861 + document.getElementById('close-results').addEventListener('click', function() {
  862 + document.getElementById('results-panel').classList.add('hidden');
  863 + });
  864 +
  865 + /**
  866 + * 显示搜索结果表格
  867 + * @param {Array} data - 搜索结果数据数组
  868 + */
  869 + function displayResults(data) {
  870 + var tbody = document.getElementById('results-tbody');
  871 + tbody.innerHTML = '';
  872 +
  873 + data.forEach(function(item, index) {
  874 + var row = document.createElement('tr');
  875 + row.className = 'border-b border-[#eee] hover:bg-[#f5f8fc]';
  876 + row.innerHTML =
  877 + '<td class="px-2 py-2">' + (index + 1) + '</td>' +
  878 + '<td class="px-2 py-2">' + item.nbbm + '</td>' +
  879 + '<td class="px-2 py-2">' + item.deviceId + '</td>' +
  880 + '<td class="px-2 py-2">' + item.lineId + '</td>' +
  881 + '<td class="px-2 py-2">' + (item.upDown === 1 ? '上行' : '下行') + '</td>';
  882 + tbody.appendChild(row);
  883 + });
  884 +
  885 + document.getElementById('results-panel').classList.remove('hidden');
  886 + }
  887 +
  888 + /**
  889 + * 监听文档点击,当点击搜索面板外部时收拢条件项
  890 + */
  891 + document.addEventListener('click', function(e) {
  892 + var searchPanel = document.getElementById('search-panel-container');
  893 + if (searchPanel && !searchPanel.contains(e.target)) {
  894 + if (conditionPanel.classList.contains('open')) {
  895 + toggleConditionPanel();
  896 + }
  897 + }
  898 + });
  899 + });
  900 + </script>
  901 +</body>
  902 +</html>
... ...