data.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. const SJZ_GOV = { lng: 116.21693, lat: 39.90446 }; // 石景山区演示落点(可后续替换更精确POI)
  2. function randn() {
  3. // 简单正态近似
  4. let u = 0, v = 0;
  5. while (u === 0) u = Math.random();
  6. while (v === 0) v = Math.random();
  7. return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
  8. }
  9. export function makePoints(n = 200) {
  10. // 做“带簇”的分布,更像真实路口
  11. const clusters = [
  12. { dx: 0.00, dy: 0.00, w: 0.45 },
  13. { dx: 0.03, dy: 0.01, w: 0.25 },
  14. { dx: -0.02, dy: 0.02, w: 0.18 },
  15. { dx: 0.01, dy: -0.03, w: 0.12 },
  16. ];
  17. function pickCluster() {
  18. const r = Math.random();
  19. let acc = 0;
  20. for (const c of clusters) { acc += c.w; if (r <= acc) return c; }
  21. return clusters[0];
  22. }
  23. const pts = [];
  24. for (let i = 0; i < n; i++) {
  25. const c = pickCluster();
  26. const lng = SJZ_GOV.lng + c.dx + randn() * 0.008;
  27. const lat = SJZ_GOV.lat + c.dy + randn() * 0.006;
  28. const statusRoll = Math.random();
  29. const status = statusRoll < 0.78 ? "normal" : (statusRoll < 0.92 ? "busy" : "alarm");
  30. pts.push({
  31. id: `JUNC_${String(i + 1).padStart(3, "0")}`,
  32. name: `路口-${i + 1}`,
  33. lng: Number(lng.toFixed(6)),
  34. lat: Number(lat.toFixed(6)),
  35. status,
  36. updatedAt: Date.now() - Math.floor(Math.random() * 120000),
  37. });
  38. }
  39. return pts;
  40. }
  41. // ====== 干路监控 mock 数据 ======
  42. // 模拟 API 请求获取树形数据
  43. export function menuData(tabId) {
  44. return new Promise(resolve => {
  45. // 模拟网络延迟 500ms
  46. setTimeout(() => {
  47. if (tabId === 'arterial') {
  48. resolve([
  49. {
  50. id: 'm1', label: '主控中心', isOpen: true,
  51. children: [
  52. {
  53. id: 'm1-1', label: '北京市交警总队', isOpen: true,
  54. children: [
  55. {
  56. id: 'c1', label: '石景山区', isOpen: true,
  57. children: [
  58. { id: 'l1', label: 'xxx 绿波带1' },
  59. { id: 'l2', label: 'xxx 绿波带2' },
  60. { id: 'l3', label: 'xxx 绿波带3' },
  61. { id: 'l4', label: 'xxx 绿波带4' }
  62. ]
  63. },
  64. {
  65. id: 'c2', label: '丰台区', isOpen: false,
  66. children: [{ id: 'l5', label: '丰台绿波带1' }]
  67. },
  68. {
  69. id: 'c3', label: '门头沟区', isOpen: false,
  70. children: [{ id: 'l6', label: '门头沟绿波带1' }]
  71. },
  72. {
  73. id: 'c4', label: '石景山区', isOpen: true,
  74. children: [
  75. { id: 'l1', label: 'xxx 绿波带1' },
  76. { id: 'l2', label: 'xxx 绿波带2' },
  77. { id: 'l3', label: 'xxx 绿波带3' },
  78. { id: 'l4', label: 'xxx 绿波带4' }
  79. ]
  80. },
  81. {
  82. id: 'c5', label: '丰台区', isOpen: false,
  83. children: [{ id: 'l5', label: '丰台绿波带1' }]
  84. },
  85. {
  86. id: 'c6', label: '门头沟区', isOpen: false,
  87. children: [{ id: 'l6', label: '门头沟绿波带1' }]
  88. },
  89. {
  90. id: 'c7', label: '石景山区', isOpen: true,
  91. children: [
  92. { id: 'l1', label: 'xxx 绿波带1' },
  93. { id: 'l2', label: 'xxx 绿波带2' },
  94. { id: 'l3', label: 'xxx 绿波带3' },
  95. { id: 'l4', label: 'xxx 绿波带4' }
  96. ]
  97. },
  98. {
  99. id: 'c8', label: '丰台区', isOpen: false,
  100. children: [{ id: 'l5', label: '丰台绿波带1' }]
  101. },
  102. {
  103. id: 'c9', label: '门头沟区', isOpen: false,
  104. children: [{ id: 'l6', label: '门头沟绿波带1' }]
  105. }
  106. ]
  107. }
  108. ]
  109. }
  110. ]);
  111. } else {
  112. resolve([
  113. {
  114. id: 'other1', label: `${tabId} 控制台`, isOpen: true,
  115. children: [
  116. { id: 'o-c1', label: '测试节点 A' },
  117. { id: 'o-c2', label: '测试节点 B' }
  118. ]
  119. }
  120. ]);
  121. }
  122. }, 500);
  123. });
  124. }
  125. // ====== 交通时序图 mock 数据 ======
  126. const trafficIntersections = [
  127. '段祺瑞执政府', '剪子巷', '宽街', '北河沿',
  128. '锣鼓巷', '东板桥胡同', '地安门', '北海后门', '郭沫若故居'
  129. ];
  130. const trafficDistances = [0, 300, 600, 900, 1200, 1500, 1800, 2100, 2400];
  131. export function makeTrafficTimeSpaceData(opts = {}) {
  132. const {
  133. speed = 15,
  134. cycle = 120,
  135. band = 40,
  136. totalTime = 1800,
  137. maxDistance = 2400,
  138. dists = trafficDistances
  139. } = opts;
  140. const waveData = [];
  141. const greenData = [];
  142. for (let t = 0; t <= totalTime; t += cycle) {
  143. const downStart = t + cycle / 2;
  144. waveData.push({
  145. yBottom: 0, yTop: maxDistance,
  146. xBL: t, xBR: t + band,
  147. xTL: t + maxDistance / speed, xTR: t + maxDistance / speed + band,
  148. label: Math.round(speed * 3.6) + 'km/h', direction: 'up'
  149. });
  150. waveData.push({
  151. yBottom: maxDistance, yTop: 0,
  152. xBL: downStart, xBR: downStart + band,
  153. xTL: downStart + maxDistance / speed, xTR: downStart + maxDistance / speed + band,
  154. label: Math.round(speed * 0.9 * 3.6) + 'km/h', direction: 'down'
  155. });
  156. dists.forEach(y => {
  157. greenData.push({ y, start: t + y / speed, end: t + y / speed + band });
  158. greenData.push({ y, start: downStart + (maxDistance - y) / speed, end: downStart + (maxDistance - y) / speed + band });
  159. });
  160. }
  161. return {
  162. intersections: trafficIntersections,
  163. distances: trafficDistances,
  164. waveData,
  165. greenData
  166. };
  167. }
  168. /**
  169. * 模拟获取交通配时方案数据的 API 请求
  170. */
  171. export function fetchSignalTimingData(id) {
  172. console.log('id', id);
  173. return new Promise((resolve) => {
  174. // 模拟 500ms 的网络请求延迟
  175. setTimeout(() => {
  176. resolve({
  177. code: 200,
  178. message: 'success',
  179. data: {
  180. cycleLength: 140,
  181. currentTime: 67,
  182. // [轨道(1上,0下), 开始时间, 结束时间, 相位名称, 时长, 颜色类型, 图标类型]
  183. phaseData: [
  184. // 单轨道 P1-P4
  185. [0, 0, 27, 'P1', 27, 'green', 'UP'],
  186. [0, 27, 30, '', null, 'stripe', null],
  187. [0, 30, 32, '', null, 'yellow', null],
  188. [0, 32, 35, '', null, 'red', null],
  189. [0, 35, 62, 'P2', 27, 'green', 'TURN_LEFT'],
  190. [0, 62, 65, '', null, 'stripe', null],
  191. [0, 65, 67, '', null, 'yellow', null],
  192. [0, 67, 70, '', null, 'red', null],
  193. [0, 70, 97, 'P3', 27, 'green', 'DOWN'],
  194. [0, 97, 100, '', null, 'stripe', null],
  195. [0, 100, 102, '', null, 'yellow', null],
  196. [0, 102, 105, '', null, 'red', null],
  197. [0, 105, 132, 'P4', 27, 'green', 'TURN_RIGHT'],
  198. [0, 132, 135, '', null, 'stripe', null],
  199. [0, 135, 137, '', null, 'yellow', null],
  200. [0, 137, 140, '', null, 'red', null],
  201. ]
  202. }
  203. });
  204. }, 500);
  205. });
  206. }
  207. /**
  208. * 模拟获取路口信号相位与流量监控图数据的 API 请求
  209. */
  210. export function getIntersectionData(id) {
  211. console.log('id', id);
  212. return new Promise((resolve) => {
  213. // 模拟 500ms 的网络延迟
  214. setTimeout(() => {
  215. resolve({
  216. // 信号灯当前相位与倒计时状态
  217. signals: {
  218. ns: { phaseName: '相位3', time: 38, isGreen: true }, // 南北向
  219. ew: { phaseName: '相位7', time: 38, isGreen: false } // 东西向
  220. },
  221. // 交叉路口四个方向的静态配置 (N北, S南, E东, W西)
  222. armsConfig: {
  223. N: {
  224. cameraType: 1, // 1: 枪机, 2: 球机, 0: 无摄像头
  225. // 车道指示标,从左到右(从中心黄线到路沿)排列。支持: 'U'调头, 'L'左转, 'S'直行, 'R'右转, null无
  226. lanes: ['U', 'L', 'S', 'R']
  227. },
  228. S: {
  229. cameraType: 1,
  230. lanes: [null, 'L', 'S', 'R'] // 最内侧车道无指示标
  231. },
  232. E: {
  233. cameraType: 2,
  234. lanes: [null, 'L', 'S', null]
  235. },
  236. W: {
  237. cameraType: 0,
  238. lanes: ['U', 'L', 'S', null]
  239. }
  240. }
  241. });
  242. }, 500);
  243. });
  244. }
  245. // ====== 首页 mock 数据 ======
  246. export function makeHomeData() {
  247. // 这里做“看起来真”的波动
  248. const onlineTotal = 1000;
  249. const online = 960 + Math.floor(Math.random() * 35);
  250. const offline = onlineTotal - online;
  251. const alarms = [
  252. { id: "A001", title: "通讯中断", time: "16:28:28", loc: "中关村大街-科学南路路口", level: "high" },
  253. { id: "A002", title: "降级黄闪", time: "16:28:28", loc: "中关村大街-科学南路路口", level: "mid" },
  254. { id: "A003", title: "信号机离线", time: "16:18:02", loc: "阜石路-石景山路路口", level: "mid" },
  255. { id: "A004", title: "检测器异常", time: "16:11:09", loc: "鲁谷路-政达路路口", level: "low" },
  256. ];
  257. const duty = Array.from({ length: 5 }).map((_, idx) => ({
  258. id: `D${idx + 1}`,
  259. name: idx === 0 ? "测试" : ["张飞", "关将", "刘备", "孙权"][idx - 1],
  260. executor: idx === 0 ? "测试" : ["张飞", "关将", "刘备", "孙权"][idx - 1],
  261. level: idx === 2 ? "二级" : "一级",
  262. status: idx === 2 ? "进行中" : "未开始",
  263. }));
  264. return {
  265. header: {
  266. title: "交通信号控制平台",
  267. timeText: new Date().toLocaleTimeString(),
  268. dateText: new Date().toLocaleDateString(),
  269. weatherText: "晴 32/17℃",
  270. },
  271. online: { online, offline, total: onlineTotal, rate: Math.round((online / onlineTotal) * 100) },
  272. alarms,
  273. duty,
  274. device: { normal: 998, fault: 0 },
  275. };
  276. }