主页新增设备,传输线分析仪,可以分析双对线缆,根据自定义线缆长度,材质,线芯直径,绝缘厚度,绞距,节距等参数,了解影响IL,RL,NEXT等影响规律

This commit is contained in:
2025-11-13 08:18:22 +00:00
parent a66a3c38e2
commit 36aff7a4c9
335 changed files with 4259 additions and 23337 deletions

View File

@@ -1,65 +1,35 @@
import React, { useEffect } from 'react';
import { getAssetUrl } from '@/utils/asset';
import { HomePage, Project, Operators, CableId, Tools, Result, TestConfig, MenuList, Testing, ResultInfo, CopperPerformance } from '@/components/dsxpage';
import Toast from './lib/Toast';
import useDisplayStore from '@/store/displayStore';
import { useAudio } from '@/components/AudioProvider';
import React from 'react';
import useDeviceStore from '@/store/deviceStore';
import DSXDisplay from './DisplayMainPage/dsx';
import EstAnalyzerDisplay from './DisplayMainPage/est-analyzer';
import WiFiDisplay from './DisplayMainPage/wifi';
export default function DisPlay() {
const { navigation, navigateTo, toastMessage} = useDisplayStore();
const { play } = useAudio();
const { installedDevices } = useDeviceStore();
const renderPage = () => {
const pageName = navigation?.current?.name || 'home';
// 根据安装的主设备ID渲染不同的主页
const renderMainPage = () => {
const mainDeviceId = installedDevices?.main?.id;
switch (pageName) {
case 'home':
return <HomePage />;
case 'project':
return <Project />;
case 'operators':
return <Operators />;
case 'cableId':
return <CableId />;
case 'result':
return <Result />;
case 'tools':
return <Tools />;
case 'testConfig':
return <TestConfig />;
case 'menulist':
return <MenuList/>
case 'testing':
return <Testing/>
case 'resultinfo':
return <ResultInfo/>
case 'copperperformance':
return <CopperPerformance/>
// 可以添加更多页面
switch (mainDeviceId) {
case 'dsx':
return <DSXDisplay />;
case 'est-analyzer':
return <EstAnalyzerDisplay />;
case 'wifi':
return <WiFiDisplay />;
default:
return <HomePage />;
// 如果没有安装主设备,显示默认页面
return (
<div className="w-[480px] h-[640px] flex flex-col overflow-hidden items-center justify-center">
<div className="text-center">
<h1 className="text-xl font-bold text-[#0ff] mb-4">请安装设备</h1>
<p className="text-gray-600">请从左侧拖拽一个主设备到工作区域</p>
</div>
</div>
);
}
};
const handleClick = () => {
if (touchSound) {
touchSound.currentTime = 0;
touchSound.play();
play('keyClick');
}
};
return (
<div
className="w-[480px] h-[640px] bg-[#fff] flex flex-col overflow-hidden"
onClick={handleClick}
>
{toastMessage && <Toast />}
{renderPage()}
</div>
);
return renderMainPage();
}

View File

@@ -0,0 +1,504 @@
const ILData = {
1: 0.4,
2: 0.4,
3: 0.5,
4: 0.6,
5: 0.7,
6: 0.7,
7: 0.8,
8: 0.9,
9: 0.9,
10: 1.0,
11: 1.0,
12: 1.1,
13: 1.1,
14: 1.2,
15: 1.2,
16: 1.2,
17: 1.3,
18: 1.3,
19: 1.4,
20: 1.4,
21: 1.4,
22: 1.5,
23: 1.5,
24: 1.5,
25: 1.6,
26: 1.6,
27: 1.6,
28: 1.7,
29: 1.7,
30: 1.7,
31: 1.7,
32: 1.8,
33: 1.8,
34: 1.8,
35: 1.9,
36: 1.9,
37: 1.9,
38: 1.9,
39: 2.0,
40: 2.0,
41: 2.0,
42: 2.0,
43: 2.1,
44: 2.1,
45: 2.1,
46: 2.1,
47: 2.2,
48: 2.2,
49: 2.2,
50: 2.2,
51: 2.3,
52: 2.3,
53: 2.3,
54: 2.3,
55: 2.3,
56: 2.4,
57: 2.4,
58: 2.4,
59: 2.4,
60: 2.5,
61: 2.5,
62: 2.5,
63: 2.5,
64: 2.5,
65: 2.6,
66: 2.6,
67: 2.6,
68: 2.6,
69: 2.6,
70: 2.7,
71: 2.7,
72: 2.7,
73: 2.7,
74: 2.7,
75: 2.8,
76: 2.8,
77: 2.8,
78: 2.8,
79: 2.8,
80: 2.8,
81: 2.9,
82: 2.9,
83: 2.9,
84: 2.9,
85: 2.9,
86: 3.0,
87: 3.0,
88: 3.0,
89: 3.0,
90: 3.0,
91: 3.1,
92: 3.1,
93: 3.1,
94: 3.1,
95: 3.1,
96: 3.1,
97: 3.2,
98: 3.2,
99: 3.2,
100: 3.2,
101: 3.2,
102: 3.2,
103: 3.3,
104: 3.3,
105: 3.3,
106: 3.3,
107: 3.3,
108: 3.3,
109: 3.4,
110: 3.4,
111: 3.4,
112: 3.4,
113: 3.4,
114: 3.4,
115: 3.4,
116: 3.5,
117: 3.5,
118: 3.5,
119: 3.5,
120: 3.5,
121: 3.5,
122: 3.6,
123: 3.6,
124: 3.6,
125: 3.6,
126: 3.6,
127: 3.6,
128: 3.6,
129: 3.7,
130: 3.7,
131: 3.7,
132: 3.7,
133: 3.7,
134: 3.7,
135: 3.7,
136: 3.8,
137: 3.8,
138: 3.8,
139: 3.8,
140: 3.8,
141: 3.8,
142: 3.9,
143: 3.9,
144: 3.9,
145: 3.9,
146: 3.9,
147: 3.9,
148: 4.0,
149: 4.0,
150: 4.0,
151: 4.0,
152: 4.0,
153: 4.0,
154: 4.0,
155: 4.0,
156: 4.1,
157: 4.1,
158: 4.1,
159: 4.1,
160: 4.2,
161: 4.2,
162: 4.2,
163: 4.2,
164: 4.2,
165: 4.2,
166: 4.2,
167: 4.2,
168: 4.2,
169: 4.3,
170: 4.3,
171: 4.3,
172: 4.3,
173: 4.3,
174: 4.3,
175: 4.3,
176: 4.3,
177: 4.3,
178: 4.4,
179: 4.4,
180: 4.4,
181: 4.4,
182: 4.4,
183: 4.4,
184: 4.5,
185: 4.5,
186: 4.5,
187: 4.5,
188: 4.5,
189: 4.5,
190: 4.5,
191: 4.6,
192: 4.6,
193: 4.6,
194: 4.7,
195: 4.7,
196: 4.6,
197: 4.6,
198: 4.7,
199: 4.7,
200: 4.7,
201: 4.7,
202: 4.7,
203: 4.8,
204: 4.8,
205: 4.8,
206: 4.8,
207: 4.8,
208: 4.8,
209: 4.8,
210: 4.8,
211: 4.8,
212: 4.8,
213: 4.9,
214: 4.9,
215: 4.9,
216: 4.9,
217: 4.9,
218: 4.9,
219: 4.9,
220: 4.9,
221: 4.9,
222: 4.9,
223: 4.9,
224: 5.0,
225: 5.0,
226: 5.0,
227: 5.0,
228: 5.0,
229: 5.0,
230: 5.0,
231: 5.0,
232: 5.0,
233: 5.0,
234: 5.1,
235: 5.1,
236: 5.1,
237: 5.1,
238: 5.1,
239: 5.1,
240: 5.1,
241: 5.1,
242: 5.1,
243: 5.2,
244: 5.2,
245: 5.2,
246: 5.2,
247: 5.2,
248: 5.2,
249: 5.3,
250: 5.3,
251: 5.3,
252: 5.3,
253: 5.3,
254: 5.3,
255: 5.3,
256: 5.3,
257: 5.3,
258: 5.3,
259: 5.4,
260: 5.4,
261: 5.4,
262: 5.4,
263: 5.4,
264: 5.4,
265: 5.5,
266: 5.5,
267: 5.4,
268: 5.4,
269: 5.4,
270: 5.4,
271: 5.5,
272: 5.4,
273: 5.4,
274: 5.5,
275: 5.5,
276: 5.5,
277: 5.5,
278: 5.5,
279: 5.5,
280: 5.5,
281: 5.5,
282: 5.5,
283: 5.5,
284: 5.5,
285: 5.5,
286: 5.5,
287: 5.5,
288: 5.5,
289: 5.6,
290: 5.6,
291: 5.6,
292: 5.6,
293: 5.6,
294: 5.6,
295: 5.6,
296: 5.7,
297: 5.7,
298: 5.7,
299: 5.7,
300: 5.7,
301: 5.7,
302: 5.7,
303: 5.7,
304: 5.7,
305: 5.8,
306: 5.8,
307: 5.8,
308: 5.7,
309: 5.8,
310: 5.8,
311: 5.8,
312: 5.8,
313: 5.8,
314: 5.8,
315: 5.8,
316: 5.9,
317: 5.9,
318: 5.9,
319: 5.9,
320: 5.9,
321: 6.0,
322: 6.0,
323: 6.0,
324: 6.0,
325: 5.9,
326: 5.9,
327: 6.0,
328: 6.0,
329: 6.0,
330: 6.0,
331: 6.0,
332: 6.0,
333: 6.0,
334: 6.0,
335: 6.0,
336: 6.0,
337: 6.0,
338: 6.0,
339: 6.0,
340: 6.0,
341: 6.0,
342: 6.0,
343: 6.0,
344: 6.1,
345: 6.1,
346: 6.1,
347: 6.1,
348: 6.1,
349: 6.1,
350: 6.1,
351: 6.1,
352: 6.1,
353: 6.2,
354: 6.2,
355: 6.2,
356: 6.2,
357: 6.2,
358: 6.2,
359: 6.2,
360: 6.3,
361: 6.3,
362: 6.3,
363: 6.3,
364: 6.3,
365: 6.3,
366: 6.3,
367: 6.3,
368: 6.4,
369: 6.4,
370: 6.3,
371: 6.4,
372: 6.4,
373: 6.4,
374: 6.4,
375: 6.4,
376: 6.4,
377: 6.4,
378: 6.4,
379: 6.4,
380: 6.4,
381: 6.4,
382: 6.4,
383: 6.4,
384: 6.4,
385: 6.4,
386: 6.4,
387: 6.4,
388: 6.4,
389: 6.5,
390: 6.5,
391: 6.4,
392: 6.4,
393: 6.5,
394: 6.5,
395: 6.5,
396: 6.5,
397: 6.5,
398: 6.5,
399: 6.5,
400: 6.5,
401: 6.5,
402: 6.5,
403: 6.5,
404: 6.5,
405: 6.5,
406: 6.5,
407: 6.6,
408: 6.6,
409: 6.6,
410: 6.6,
411: 6.6,
412: 6.6,
413: 6.6,
414: 6.6,
415: 6.6,
416: 6.6,
417: 6.6,
418: 6.7,
419: 6.7,
420: 6.7,
421: 6.7,
422: 6.7,
423: 6.7,
424: 6.7,
425: 6.7,
426: 6.7,
427: 6.7,
428: 6.7,
429: 6.7,
430: 6.7,
431: 6.7,
432: 6.7,
433: 6.7,
434: 6.8,
435: 6.8,
436: 6.8,
437: 6.8,
438: 6.7,
439: 6.7,
440: 6.8,
441: 6.8,
442: 6.8,
443: 6.8,
444: 6.8,
445: 6.8,
446: 6.8,
447: 6.8,
448: 6.8,
449: 6.8,
450: 6.8,
451: 6.8,
452: 6.8,
453: 6.8,
454: 6.9,
455: 6.9,
456: 6.9,
457: 6.9,
458: 6.9,
459: 6.9,
460: 6.9,
461: 7.0,
462: 7.0,
463: 7.0,
464: 7.0,
465: 7.0,
466: 7.0,
467: 7.0,
468: 7.0,
469: 7.1,
470: 7.1,
471: 7.1,
472: 7.1,
473: 7.1,
474: 7.2,
475: 7.2,
476: 7.2,
477: 7.2,
478: 7.2,
479: 7.2,
480: 7.2,
481: 7.2,
482: 7.3,
483: 7.3,
484: 7.3,
485: 7.3,
486: 7.4,
487: 7.4,
488: 7.4,
489: 7.4,
490: 7.4,
491: 7.4,
492: 7.4,
493: 7.4,
494: 7.4,
495: 7.5,
496: 7.5,
497: 7.5,
498: 7.5,
499: 7.5,
500: 7.5,
};
export default ILData;

View File

@@ -0,0 +1,504 @@
const NEXTData = {
1: 99.1,
2: 89.5,
3: 84.4,
4: 79.7,
5: 78.1,
6: 77.9,
7: 82.2,
8: 94.6,
9: 79.5,
10: 73.2,
11: 69.4,
12: 67.9,
13: 67.5,
14: 68.5,
15: 70.8,
16: 75.6,
17: 82.0,
18: 85.5,
19: 85.4,
20: 80.5,
21: 74.9,
22: 70.7,
23: 68.6,
24: 67.7,
25: 68.6,
26: 71.1,
27: 76.8,
28: 80.7,
29: 74.7,
30: 72.0,
31: 73.0,
32: 78.9,
33: 82.2,
34: 70.6,
35: 65.9,
36: 63.9,
37: 63.5,
38: 64.9,
39: 69.0,
40: 74.7,
41: 67.9,
42: 63.6,
43: 61.8,
44: 62.0,
45: 64.7,
46: 71.7,
47: 74.9,
48: 64.9,
49: 61.2,
50: 60.0,
51: 60.6,
52: 61.9,
53: 61.8,
54: 59.6,
55: 57.3,
56: 55.9,
57: 55.8,
58: 57.3,
59: 60.3,
60: 63.0,
61: 61.4,
62: 58.5,
63: 56.9,
64: 57.1,
65: 59.2,
66: 64.0,
67: 68.1,
68: 64.4,
69: 61.7,
70: 61.6,
71: 64.9,
72: 72.0,
73: 63.7,
74: 58.9,
75: 56.6,
76: 55.9,
77: 57.0,
78: 60.4,
79: 66.1,
80: 61.8,
81: 57.7,
82: 55.8,
83: 55.6,
84: 57.3,
85: 62.3,
86: 68.3,
87: 60.4,
88: 56.4,
89: 54.9,
90: 55.1,
91: 57.0,
92: 61.9,
93: 77.8,
94: 67.1,
95: 62.4,
96: 61.7,
97: 65.3,
98: 79.1,
99: 63.0,
100: 57.6,
101: 55.2,
102: 54.7,
103: 55.5,
104: 58.0,
105: 64.2,
106: 75.9,
107: 62.8,
108: 59.6,
109: 58.9,
110: 60.2,
111: 65.0,
112: 100.7,
113: 64.4,
114: 59.4,
115: 57.2,
116: 55.9,
117: 55.4,
118: 55.4,
119: 55.1,
120: 54.8,
121: 55.5,
122: 57.8,
123: 59.7,
124: 59.3,
125: 57.8,
126: 56.3,
127: 55.8,
128: 57.2,
129: 61.2,
130: 70.5,
131: 68.7,
132: 61.2,
133: 59.2,
134: 60.2,
135: 59.8,
136: 55.9,
137: 53.3,
138: 51.4,
139: 50.5,
140: 50.8,
141: 53.1,
142: 57.5,
143: 56.8,
144: 53.4,
145: 51.3,
146: 50.5,
147: 51.2,
148: 53.7,
149: 56.0,
150: 55.5,
151: 54.6,
152: 54.3,
153: 54.8,
154: 56.4,
155: 56.7,
156: 54.0,
157: 52.2,
158: 52.3,
159: 54.0,
160: 57.3,
161: 60.2,
162: 57.2,
163: 54.0,
164: 53.1,
165: 54.4,
166: 57.0,
167: 58.0,
168: 56.7,
169: 54.9,
170: 53.7,
171: 55.0,
172: 59.3,
173: 61.9,
174: 56.9,
175: 53.7,
176: 51.5,
177: 50.4,
178: 50.9,
179: 53.0,
180: 57.3,
181: 68.1,
182: 62.0,
183: 54.9,
184: 52.7,
185: 53.2,
186: 55.9,
187: 59.0,
188: 55.4,
189: 51.0,
190: 48.9,
191: 49.3,
192: 52.7,
193: 61.4,
194: 60.8,
195: 52.8,
196: 49.7,
197: 49.4,
198: 52.3,
199: 61.7,
200: 57.0,
201: 50.6,
202: 48.2,
203: 48.1,
204: 50.5,
205: 57.9,
206: 58.4,
207: 51.1,
208: 48.6,
209: 48.3,
210: 49.8,
211: 54.1,
212: 56.3,
213: 51.3,
214: 49.1,
215: 48.9,
216: 49.9,
217: 51.5,
218: 51.5,
219: 49.3,
220: 47.8,
221: 47.9,
222: 49.8,
223: 53.2,
224: 56.6,
225: 54.7,
226: 51.5,
227: 50.3,
228: 50.3,
229: 49.9,
230: 48.7,
231: 48.0,
232: 47.5,
233: 46.4,
234: 45.1,
235: 44.5,
236: 44.6,
237: 45.6,
238: 47.7,
239: 49.8,
240: 48.6,
241: 47.3,
242: 47.3,
243: 47.3,
244: 47.7,
245: 48.2,
246: 46.9,
247: 45.3,
248: 45.3,
249: 47.1,
250: 50.1,
251: 52.0,
252: 48.3,
253: 44.8,
254: 43.7,
255: 44.7,
256: 46.7,
257: 47.4,
258: 46.3,
259: 44.5,
260: 43.3,
261: 43.8,
262: 46.0,
263: 48.7,
264: 47.8,
265: 45.2,
266: 43.6,
267: 43.4,
268: 45.0,
269: 48.5,
270: 53.9,
271: 52.7,
272: 48.5,
273: 46.4,
274: 45.8,
275: 46.5,
276: 47.6,
277: 47.5,
278: 46.2,
279: 45.3,
280: 45.1,
281: 46.1,
282: 48.3,
283: 50.4,
284: 49.0,
285: 46.6,
286: 45.5,
287: 45.6,
288: 46.8,
289: 48.6,
290: 48.7,
291: 47.2,
292: 46.6,
293: 47.6,
294: 50.8,
295: 60.9,
296: 55.9,
297: 48.3,
298: 45.6,
299: 45.3,
300: 46.7,
301: 50.4,
302: 55.8,
303: 49.8,
304: 45.8,
305: 44.5,
306: 44.6,
307: 45.2,
308: 45.7,
309: 45.2,
310: 43.4,
311: 42.1,
312: 41.8,
313: 42.4,
314: 43.7,
315: 45.2,
316: 44.9,
317: 43.2,
318: 42.0,
319: 41.8,
320: 42.4,
321: 44.1,
322: 47.4,
323: 51.4,
324: 52.4,
325: 52.8,
326: 54.2,
327: 51.9,
328: 48.7,
329: 46.2,
330: 44.8,
331: 45.0,
332: 46.5,
333: 47.8,
334: 47.2,
335: 46.0,
336: 45.2,
337: 45.3,
338: 46.1,
339: 46.2,
340: 44.4,
341: 42.2,
342: 40.8,
343: 40.1,
344: 39.9,
345: 40.0,
346: 40.4,
347: 41.0,
348: 41.9,
349: 43.2,
350: 44.5,
351: 45.6,
352: 47.6,
353: 51.0,
354: 51.7,
355: 48.4,
356: 45.5,
357: 43.8,
358: 44.0,
359: 46.1,
360: 47.9,
361: 45.2,
362: 42.1,
363: 40.2,
364: 39.7,
365: 40.6,
366: 42.8,
367: 44.6,
368: 43.1,
369: 41.3,
370: 40.9,
371: 42.3,
372: 46.0,
373: 51.8,
374: 48.1,
375: 44.6,
376: 44.1,
377: 45.8,
378: 49.3,
379: 49.1,
380: 45.1,
381: 43.4,
382: 44.6,
383: 49.5,
384: 51.6,
385: 43.7,
386: 39.6,
387: 37.8,
388: 37.5,
389: 38.4,
390: 39.9,
391: 40.8,
392: 40.4,
393: 39.5,
394: 38.9,
395: 39.0,
396: 39.6,
397: 40.3,
398: 41.1,
399: 42.0,
400: 42.9,
401: 43.7,
402: 44.3,
403: 44.7,
404: 45.4,
405: 47.4,
406: 51.5,
407: 59.4,
408: 68.2,
409: 63.1,
410: 58.6,
411: 50.9,
412: 45.7,
413: 42.8,
414: 41.7,
415: 42.2,
416: 44.9,
417: 49.0,
418: 46.8,
419: 42.5,
420: 40.3,
421: 40.0,
422: 41.7,
423: 44.6,
424: 43.8,
425: 40.6,
426: 38.8,
427: 38.6,
428: 40.6,
429: 45.5,
430: 47.4,
431: 41.6,
432: 38.6,
433: 37.7,
434: 38.5,
435: 41.2,
436: 45.2,
437: 43.2,
438: 39.7,
439: 37.9,
440: 37.6,
441: 38.7,
442: 41.7,
443: 46.4,
444: 46.4,
445: 43.8,
446: 42.5,
447: 42.6,
448: 43.8,
449: 45.1,
450: 44.5,
451: 43.6,
452: 43.8,
453: 45.4,
454: 47.0,
455: 45.7,
456: 42.4,
457: 39.8,
458: 38.5,
459: 38.2,
460: 38.9,
461: 40.5,
462: 43.2,
463: 46.6,
464: 48.5,
465: 49.1,
466: 48.4,
467: 45.0,
468: 41.6,
469: 39.3,
470: 37.8,
471: 37.4,
472: 38.3,
473: 40.8,
474: 45.6,
475: 51.6,
476: 46.2,
477: 42.4,
478: 41.3,
479: 41.8,
480: 42.5,
481: 40.9,
482: 38.3,
483: 36.3,
484: 35.4,
485: 35.8,
486: 37.5,
487: 40.6,
488: 45.5,
489: 47.8,
490: 44.4,
491: 43.0,
492: 44.2,
493: 47.0,
494: 45.7,
495: 41.7,
496: 39.0,
497: 37.8,
498: 38.1,
499: 39.6,
500: 41.7,
};
export default NEXTData;

View File

@@ -0,0 +1,504 @@
const RLData = {
1: 26.4,
2: 25.4,
3: 27.7,
4: 36.2,
5: 37.1,
6: 30.3,
7: 28.3,
8: 28.7,
9: 32.0,
10: 36.8,
11: 35.5,
12: 31.7,
13: 30.2,
14: 30.7,
15: 32.6,
16: 35.6,
17: 36.7,
18: 34.1,
19: 32.0,
20: 31.8,
21: 33.1,
22: 37.4,
23: 44.6,
24: 39.8,
25: 36.2,
26: 35.5,
27: 37.5,
28: 43.6,
29: 50.9,
30: 40.9,
31: 38.0,
32: 37.8,
33: 40.0,
34: 45.6,
35: 55.8,
36: 45.0,
37: 40.6,
38: 39.1,
39: 39.0,
40: 40.7,
41: 44.7,
42: 53.2,
43: 48.6,
44: 42.9,
45: 40.8,
46: 41.2,
47: 43.7,
48: 47.0,
49: 45.0,
50: 41.5,
51: 39.8,
52: 40.0,
53: 42.6,
54: 47.7,
55: 47.2,
56: 41.1,
57: 38.2,
58: 37.1,
59: 38.1,
60: 40.9,
61: 45.1,
62: 45.0,
63: 41.5,
64: 40.2,
65: 41.4,
66: 45.6,
67: 52.7,
68: 45.8,
69: 41.1,
70: 39.2,
71: 38.8,
72: 39.4,
73: 40.8,
74: 43.1,
75: 46.7,
76: 51.9,
77: 49.0,
78: 45.2,
79: 44.0,
80: 44.2,
81: 43.8,
82: 41.0,
83: 37.9,
84: 36.2,
85: 35.9,
86: 37.4,
87: 41.6,
88: 60.0,
89: 42.6,
90: 36.6,
91: 33.9,
92: 33.0,
93: 33.5,
94: 35.5,
95: 39.1,
96: 40.6,
97: 37.1,
98: 34.1,
99: 32.7,
100: 32.6,
101: 33.8,
102: 36.1,
103: 40.4,
104: 49.5,
105: 47.7,
106: 41.9,
107: 39.6,
108: 39.0,
109: 39.5,
110: 40.7,
111: 43.2,
112: 46.4,
113: 46.6,
114: 40.7,
115: 36.3,
116: 33.6,
117: 32.3,
118: 32.3,
119: 33.9,
120: 38.1,
121: 47.2,
122: 40.6,
123: 35.4,
124: 34.0,
125: 34.6,
126: 37.0,
127: 38.2,
128: 35.1,
129: 32.5,
130: 31.9,
131: 33.3,
132: 38.1,
133: 50.1,
134: 37.2,
135: 32.8,
136: 31.8,
137: 33.4,
138: 40.1,
139: 42.3,
140: 32.5,
141: 28.7,
142: 27.3,
143: 27.8,
144: 30.3,
145: 35.6,
146: 41.0,
147: 34.4,
148: 31.6,
149: 31.3,
150: 33.4,
151: 38.4,
152: 48.1,
153: 43.6,
154: 42.1,
155: 45.1,
156: 41.3,
157: 35.2,
158: 32.0,
159: 30.7,
160: 30.7,
161: 31.0,
162: 29.9,
163: 28.1,
164: 27.0,
165: 26.9,
166: 28.2,
167: 30.8,
168: 33.1,
169: 31.9,
170: 30.1,
171: 29.9,
172: 31.7,
173: 36.3,
174: 44.3,
175: 38.1,
176: 34.9,
177: 35.2,
178: 39.2,
179: 45.7,
180: 39.2,
181: 36.4,
182: 38.2,
183: 48.8,
184: 39.2,
185: 32.2,
186: 29.4,
187: 29.2,
188: 32.0,
189: 41.9,
190: 35.6,
191: 28.4,
192: 25.6,
193: 24.9,
194: 26.2,
195: 29.5,
196: 34.2,
197: 32.5,
198: 29.4,
199: 29.1,
200: 31.9,
201: 42.6,
202: 35.5,
203: 28.8,
204: 26.4,
205: 26.2,
206: 28.0,
207: 32.4,
208: 37.3,
209: 34.2,
210: 32.8,
211: 34.7,
212: 36.1,
213: 30.7,
214: 26.8,
215: 24.9,
216: 25.1,
217: 27.4,
218: 33.1,
219: 38.1,
220: 30.6,
221: 27.9,
222: 28.2,
223: 31.4,
224: 38.6,
225: 33.9,
226: 28.9,
227: 28.3,
228: 30.3,
229: 34.4,
230: 33.2,
231: 28.7,
232: 26.3,
233: 26.1,
234: 28.4,
235: 35.9,
236: 40.2,
237: 31.2,
238: 29.1,
239: 30.0,
240: 37.5,
241: 41.2,
242: 30.7,
243: 27.7,
244: 28.1,
245: 31.9,
246: 56.8,
247: 31.6,
248: 26.4,
249: 24.7,
250: 25.2,
251: 28.7,
252: 39.7,
253: 35.1,
254: 28.7,
255: 27.5,
256: 30.0,
257: 39.7,
258: 32.1,
259: 25.9,
260: 23.5,
261: 23.8,
262: 27.0,
263: 38.2,
264: 31.1,
265: 24.6,
266: 22.5,
267: 22.5,
268: 24.8,
269: 30.9,
270: 37.7,
271: 28.5,
272: 25.2,
273: 24.9,
274: 26.2,
275: 28.1,
276: 28.3,
277: 27.7,
278: 28.2,
279: 30.9,
280: 32.9,
281: 29.0,
282: 25.9,
283: 24.9,
284: 26.3,
285: 30.0,
286: 31.1,
287: 26.6,
288: 24.6,
289: 24.8,
290: 27.7,
291: 33.9,
292: 29.0,
293: 24.2,
294: 22.6,
295: 22.9,
296: 25.8,
297: 31.8,
298: 30.3,
299: 25.4,
300: 23.6,
301: 24.1,
302: 27.2,
303: 32.5,
304: 30.8,
305: 27.0,
306: 26.5,
307: 28.9,
308: 35.6,
309: 34.4,
310: 28.4,
311: 26.6,
312: 27.5,
313: 32.6,
314: 36.9,
315: 28.8,
316: 25.3,
317: 25.3,
318: 28.5,
319: 37.9,
320: 29.5,
321: 24.1,
322: 22.1,
323: 22.1,
324: 24.2,
325: 28.7,
326: 31.0,
327: 27.0,
328: 25.3,
329: 26.0,
330: 28.5,
331: 28.6,
332: 25.1,
333: 23.0,
334: 22.6,
335: 23.5,
336: 25.6,
337: 25.9,
338: 24.7,
339: 24.3,
340: 25.8,
341: 28.4,
342: 27.4,
343: 23.8,
344: 21.7,
345: 21.5,
346: 23.3,
347: 28.3,
348: 35.1,
349: 30.2,
350: 27.5,
351: 28.8,
352: 33.6,
353: 32.6,
354: 26.2,
355: 23.5,
356: 23.6,
357: 26.5,
358: 39.6,
359: 30.0,
360: 23.4,
361: 21.1,
362: 21.4,
363: 24.3,
364: 29.0,
365: 25.4,
366: 21.1,
367: 19.6,
368: 19.9,
369: 22.0,
370: 23.6,
371: 22.0,
372: 19.7,
373: 18.7,
374: 19.5,
375: 21.7,
376: 24.6,
377: 23.5,
378: 21.2,
379: 20.3,
380: 21.1,
381: 23.1,
382: 24.3,
383: 23.0,
384: 21.4,
385: 21.7,
386: 24.2,
387: 30.3,
388: 32.6,
389: 26.3,
390: 23.8,
391: 23.8,
392: 25.4,
393: 26.2,
394: 24.4,
395: 22.8,
396: 22.5,
397: 23.8,
398: 25.0,
399: 24.1,
400: 22.4,
401: 22.0,
402: 22.4,
403: 24.8,
404: 26.5,
405: 25.1,
406: 23.3,
407: 23.2,
408: 25.5,
409: 29.8,
410: 27.7,
411: 23.0,
412: 20.5,
413: 20.5,
414: 22.2,
415: 25.2,
416: 25.3,
417: 22.6,
418: 21.3,
419: 21.7,
420: 24.2,
421: 28.4,
422: 30.2,
423: 26.1,
424: 24.3,
425: 25.3,
426: 29.3,
427: 42.8,
428: 33.8,
429: 27.9,
430: 26.8,
431: 29.0,
432: 34.0,
433: 31.9,
434: 27.3,
435: 25.5,
436: 26.0,
437: 29.0,
438: 34.4,
439: 31.7,
440: 27.6,
441: 26.0,
442: 26.4,
443: 27.4,
444: 27.3,
445: 26.0,
446: 25.2,
447: 25.3,
448: 26.2,
449: 26.7,
450: 26.3,
451: 25.9,
452: 26.5,
453: 27.7,
454: 27.8,
455: 25.8,
456: 24.2,
457: 23.8,
458: 24.9,
459: 26.6,
460: 26.5,
461: 24.4,
462: 23.0,
463: 22.9,
464: 24.4,
465: 27.2,
466: 28.7,
467: 27.1,
468: 25.6,
469: 25.5,
470: 26.1,
471: 25.4,
472: 23.4,
473: 21.8,
474: 21.5,
475: 22.6,
476: 25.8,
477: 31.6,
478: 32.8,
479: 28.6,
480: 27.6,
481: 29.2,
482: 32.0,
483: 29.2,
484: 25.1,
485: 22.8,
486: 22.2,
487: 22.5,
488: 23.5,
489: 24.2,
490: 24.4,
491: 24.2,
492: 24.2,
493: 24.1,
494: 23.6,
495: 22.9,
496: 22.6,
497: 22.8,
498: 23.4,
499: 24.2,
500: 25.2,
};
export default RLData;

View File

@@ -0,0 +1,65 @@
import React, { useEffect } from 'react';
import { getAssetUrl } from '@/utils/asset';
import { HomePage, Project, Operators, CableId, Tools, Result, TestConfig, MenuList, Testing, ResultInfo, CopperPerformance } from '@/components/dsxpage';
import Toast from '../lib/Toast';
import useDisplayStore from '@/store/displayStore';
import { useAudio } from '@/components/AudioProvider';
export default function DSXDisplay() {
const { navigation, navigateTo, toastMessage} = useDisplayStore();
const { play } = useAudio();
const renderPage = () => {
const pageName = navigation?.current?.name || 'home';
switch (pageName) {
case 'home':
return <HomePage />;
case 'project':
return <Project />;
case 'operators':
return <Operators />;
case 'cableId':
return <CableId />;
case 'result':
return <Result />;
case 'tools':
return <Tools />;
case 'testConfig':
return <TestConfig />;
case 'menulist':
return <MenuList/>
case 'testing':
return <Testing/>
case 'resultinfo':
return <ResultInfo/>
case 'copperperformance':
return <CopperPerformance/>
// 可以添加更多页面
default:
return <HomePage />;
}
};
const handleClick = () => {
if (touchSound) {
touchSound.currentTime = 0;
touchSound.play();
play('keyClick');
}
};
return (
<div
className="w-[480px] h-[640px] bg-[#fff] flex flex-col overflow-hidden"
onClick={handleClick}
>
{toastMessage && <Toast />}
{renderPage()}
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { useAudio } from '@/components/AudioProvider';
export default function WiFiDisplay() {
const { play } = useAudio();
const handleClick = () => {
play('keyClick');
};
return (
<div
className="w-[480px] h-[640px] bg-[#fff] flex flex-col overflow-hidden items-center justify-center"
onClick={handleClick}
>
<div className="text-center">
<h1 className="text-2xl font-bold text-gray-800 mb-4">WiFi 测试仪</h1>
<p className="text-gray-600 mb-8">无线网络测试设备主页</p>
<div className="bg-green-100 p-6 rounded-lg">
<h2 className="text-lg font-semibold text-green-800 mb-2">连接状态</h2>
<p className="text-green-600">WiFi 信号正常</p>
<div className="mt-4">
<div className="flex items-center justify-center space-x-2">
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-sm text-green-700">扫描中...</span>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -241,6 +241,11 @@ export default function JsPlumbInit() {
const testMPOBOXPortElements = document.querySelectorAll('[jstype="mpo-fiberbox-mpo"]');
const testMPOLCBOXPortElements = document.querySelectorAll('[jstype="mpo-fiberbox-lc"]');
const cableAnalyzerCopperElements = document.querySelectorAll('[jstype="cal-copper"]');
const cableAnalyzerCopperOutElements = document.querySelectorAll('[jstype="cal-copper-out"]');
// 为connector类型元素添加端点只能作为目标
connectorLeftElements.forEach((element) => {
@@ -644,6 +649,34 @@ export default function JsPlumbInit() {
});
});
// 为线缆分析铜端口添加端点
cableAnalyzerCopperElements.forEach((element) => {
const id = element.getAttribute('id');
instance.addEndpoint(element, {
anchor: 'Center',
isSource: false,
isTarget: true,
maxConnections: 1,
connectorStyle: { stroke: '#0ff', strokeWidth: 2 },
connectorHoverStyle: { stroke: '#00ff7f', strokeWidth: 3 },
paintStyle: { fill: '#0ff', },
hoverPaintStyle: { fill: '#00ff7f' }
});
});
cableAnalyzerCopperOutElements.forEach((element) => {
const id = element.getAttribute('id');
instance.addEndpoint(element, {
anchor: 'Center',
isSource: true,
isTarget: true,
maxConnections: 1,
connectorStyle: { stroke: '#0ff', strokeWidth: 2 },
connectorHoverStyle: { stroke: '#00ff7f', strokeWidth: 3 },
paintStyle: { fill: '#0ff', },
hoverPaintStyle: { fill: '#00ff7f' }
});
});
// 添加连接事件监听
instance.bind('connection', (info) => {
const sourceId = info.sourceId;

View File

@@ -0,0 +1,766 @@
import React, { useState, useEffect, useRef, useMemo } from 'react';
import useDeviceStore from '@/store/deviceStore';
const CopperAnalyzer = () => {
// 组件会自动响应参数变化重新渲染SVG
// 获取 deviceStore 中的方法
const { updateCableParams } = useDeviceStore();
// 6个核心物理参数
const [params, setParams] = useState({
cableType: 'twisted_dual', // 线缆类别
spliceMethod: 'none', // 接续方式
conductorMaterial: 'copper', // 导体材质
coreDiameter: 0.57, // 线芯直径 (mm)
insulationMaterial: 'pe', // 绝缘材料
insulationThickness: 0.2, // 绝缘厚度 (mm)
twistPitch: 12, // 绞距 (mm)
cableLength: 100, // 线缆长度 (m)
pair2TwistRatio: 1.0 // 第二对绞距比例 (相对于第一对)
});
// 正在拖拽滑块标记 & 防抖定时器
const [isDragging, setIsDragging] = useState(false);
const commitTimerRef = useRef(null);
// 导体材质选项
const conductorMaterials = [
{ value: 'copper', label: '铜 (Cu)', resistivity: 1.68e-8, color: '#CD7F32' },
{ value: 'silver', label: '银 (Ag)', resistivity: 1.59e-8, color: '#C0C0C0' },
{ value: 'aluminum', label: '铝 (Al)', resistivity: 2.82e-8, color: '#A8A8A8' },
{ value: 'gold', label: '金 (Au)', resistivity: 2.44e-8, color: '#FFD700' }
];
// 绝缘材料选项
const insulationMaterials = [
{ value: 'pe', label: 'PE (聚乙烯)', dielectric: 2.3, tanDelta: 0.0002, color: '#FF8C42' },
{ value: 'pvc', label: 'PVC (聚氯乙烯)', dielectric: 3.5, tanDelta: 0.02, color: '#FFB366' },
{ value: 'ptfe', label: 'PTFE (聚四氟乙烯)', dielectric: 2.1, tanDelta: 0.0001, color: '#FFA500' },
{ value: 'pp', label: 'PP (聚丙烯)', dielectric: 2.2, tanDelta: 0.0003, color: '#FF7F50' }
];
// AWG线径选项
const awgSizes = [
{ awg: 18, diameter: 1.024 },
{ awg: 20, diameter: 0.812 },
{ awg: 22, diameter: 0.644 },
{ awg: 24, diameter: 0.511 },
{ awg: 26, diameter: 0.405 },
{ awg: 28, diameter: 0.321 }
];
// 计算电气特性参数 R/L/C/G
const calculateElectricalParams = () => {
const conductorMat = conductorMaterials.find(m => m.value === params.conductorMaterial);
const insulationMat = insulationMaterials.find(m => m.value === params.insulationMaterial);
if (!conductorMat || !insulationMat) return { R: 0, L: 0, C: 0, G: 0 };
const r = params.coreDiameter / 2000; // 半径,转换为米
const t = params.insulationThickness / 1000; // 绝缘厚度,转换为米
const s = (params.coreDiameter + 2 * params.insulationThickness) / 1000; // 中心距,转换为米
// R - 单位长度电阻 (Ω/m)
const R_prime = conductorMat.resistivity / (Math.PI * r * r);
// L - 单位长度电感 (H/m)
const mu0 = 4 * Math.PI * 1e-7; // 真空磁导率
const L_prime = (mu0 / Math.PI) * Math.log(s / r);
// C - 单位长度电容 (F/m)
const epsilon0 = 8.854e-12; // 真空介电常数
const C_prime = (Math.PI * epsilon0 * insulationMat.dielectric) / Math.log(s / r);
// G - 单位长度电导 (S/m)
const G_prime = 2 * Math.PI * epsilon0 * insulationMat.dielectric * insulationMat.tanDelta / Math.log(s / r);
return {
R: R_prime,
L: L_prime * 1e9, // 转换为 nH/m
C: C_prime * 1e12, // 转换为 pF/m
G: G_prime * 1e9 // 转换为 nS/m
};
};
// 计算电气参数使用 useMemo避免每次轻微变动都重算
const electricalParams = useMemo(() => calculateElectricalParams(), [
params.conductorMaterial,
params.insulationMaterial,
params.coreDiameter,
params.insulationThickness
]);
// 同步参数到 deviceStore
// 将更新到全局 store 的操作做防抖,拖拽中降低更新频率,提升滑动顺滑度
useEffect(() => {
if (commitTimerRef.current) {
clearTimeout(commitTimerRef.current);
}
commitTimerRef.current = setTimeout(() => {
updateCableParams({
conductorMaterial: params.conductorMaterial,
coreDiameter: params.coreDiameter,
insulationMaterial: params.insulationMaterial,
insulationThickness: params.insulationThickness,
twistPitch: params.twistPitch,
cableLength: params.cableLength,
pair2TwistRatio: params.pair2TwistRatio,
R: electricalParams.R,
L: electricalParams.L,
C: electricalParams.C,
G: electricalParams.G
});
}, isDragging ? 120 : 60);
return () => {
if (commitTimerRef.current) {
clearTimeout(commitTimerRef.current);
}
};
}, [
params.conductorMaterial,
params.coreDiameter,
params.insulationMaterial,
params.insulationThickness,
params.twistPitch,
params.cableLength,
params.pair2TwistRatio,
electricalParams.R,
electricalParams.L,
electricalParams.C,
electricalParams.G,
isDragging,
updateCableParams
]);
// 将复杂 SVG 路径计算做 useMemo减少拖拽时的重算
const leftSplitPath0 = useMemo(() => generateWireSplitPath(0, 800, 300, true, 0, 1), [params.coreDiameter, params.twistPitch]);
const leftSplitPath1 = useMemo(() => generateWireSplitPath(1, 800, 300, true, 0, 1), [params.coreDiameter, params.twistPitch]);
const rightSplitPath0 = useMemo(() => generateWireSplitPath(0, 800, 300, false, 0, 1), [params.coreDiameter, params.twistPitch]);
const rightSplitPath1 = useMemo(() => generateWireSplitPath(1, 800, 300, false, 0, 1), [params.coreDiameter, params.twistPitch]);
const twistedPath0 = useMemo(() => generateTwistedPairPath(0, 800, 300, 0, 1), [params.coreDiameter, params.twistPitch]);
const twistedPath1 = useMemo(() => generateTwistedPairPath(1, 800, 300, 0, 1), [params.coreDiameter, params.twistPitch]);
// 第二条双绞线的垂直偏移(视图坐标)
const dualYOffset = 60;
// 第二条双绞线路径(按偏移生成)
const leftSplitPath0_2 = useMemo(() => generateWireSplitPath(0, 800, 300, true, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const leftSplitPath1_2 = useMemo(() => generateWireSplitPath(1, 800, 300, true, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const rightSplitPath0_2 = useMemo(() => generateWireSplitPath(0, 800, 300, false, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const rightSplitPath1_2 = useMemo(() => generateWireSplitPath(1, 800, 300, false, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const twistedPath0_2 = useMemo(() => generateTwistedPairPath(0, 800, 300, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const twistedPath1_2 = useMemo(() => generateTwistedPairPath(1, 800, 300, dualYOffset, params.pair2TwistRatio), [params.coreDiameter, params.twistPitch, params.pair2TwistRatio]);
const crossSectionSVG = useMemo(() => generateCrossSectionSVG(), [
params.coreDiameter,
params.insulationThickness,
params.insulationMaterial
]);
// AWG下拉选项毫米
const awgDropdownOptions = [
{ label: 'AWG 22', value: 0.644 },
{ label: 'AWG 23', value: 0.573 },
{ label: 'AWG 24', value: 0.511 },
{ label: 'AWG 25', value: 0.455 },
{ label: 'AWG 26', value: 0.405 },
];
// 动态计算左右分叉端点在容器中的百分比位置(与路径生成保持一致)
const vbWidth = 800; // SVG viewBox 宽度
const vbHeight = 150; // SVG viewBox 高度
const svgHeightForPaths = 300; // 传入路径函数的高度用于计算中心Y
const splitOffset = 12.5; // 分叉偏移,与 generateWireSplitPath 保持一致
const leftX = 30; // 左侧分叉外端 X
const rightX = vbWidth - 30; // 右侧分叉外端 X
const centerYForPaths = svgHeightForPaths * 0.1; // 与路径函数相同的中心Y
const splitMarkerPositions = {
leftTop: {
leftPct: (leftX / vbWidth) * 100,
topPct: ((centerYForPaths - splitOffset + 10) / vbHeight) * 100,
},
leftBottom: {
leftPct: (leftX / vbWidth) * 100,
topPct: ((centerYForPaths + splitOffset + 10) / vbHeight) * 100,
},
rightTop: {
leftPct: (rightX / vbWidth) * 100,
topPct: ((centerYForPaths - splitOffset + 10) / vbHeight) * 100,
},
rightBottom: {
leftPct: (rightX / vbWidth) * 100,
topPct: ((centerYForPaths + splitOffset + 10) / vbHeight) * 100,
},
};
// 第二条双绞线的分叉端点位置(按偏移计算)
const centerYForPaths2 = centerYForPaths + dualYOffset;
const splitMarkerPositions2 = {
leftTop: {
leftPct: (leftX / vbWidth) * 100,
topPct: ((centerYForPaths2 - splitOffset + 10) / vbHeight) * 90,
},
leftBottom: {
leftPct: (leftX / vbWidth) * 100,
topPct: ((centerYForPaths2 + splitOffset + 10) / vbHeight) * 90,
},
rightTop: {
leftPct: (rightX / vbWidth) * 100,
topPct: ((centerYForPaths2 - splitOffset + 10) / vbHeight) * 90,
},
rightBottom: {
leftPct: (rightX / vbWidth) * 100,
topPct: ((centerYForPaths2 + splitOffset + 10) / vbHeight) * 90,
},
};
// 生成双绞线SVG路径
function generateTwistedPairPath(wireIndex, svgWidth, svgHeight, centerYOffset = 0, twistPitchScale = 1) {
const startX = 60; // 减少左边距,为较短分叉留空间
const endX = svgWidth - 60; // 减少右边距,为较短分叉留空间
const centerY = svgHeight * 0.1 + centerYOffset; // 中心Y可按偏移调整
const amplitude = 15; // 调整振幅为15
// 根据实际绞距参数计算频率
const canvasLengthMm = 30; // 调整为30mm显示长度
const actualTwistPitch = params.twistPitch * (twistPitchScale || 1); // mm按比例缩放
// 如果绞距为0显示平行线
if (actualTwistPitch === 0) {
const parallelOffset = 3; // 平行线之间的垂直间距
const y = centerY + (wireIndex === 0 ? -parallelOffset : parallelOffset);
return `M ${startX} ${y} L ${endX} ${y}`;
}
let twistCycles = canvasLengthMm / actualTwistPitch; // 实际的绞距周期数
// 限制显示周期数,确保视觉效果合适
if (twistCycles > 20) {
twistCycles = Math.min(twistCycles, 20);
} else if (twistCycles < 1) {
twistCycles = Math.max(twistCycles, 1);
}
const frequency = (twistCycles * 2 * Math.PI) / (endX - startX);
let path = '';
for (let x = startX; x <= endX; x += 2) {
const angle = frequency * (x - startX) + wireIndex * Math.PI;
const y = centerY + amplitude * Math.sin(angle);
if (x === startX) {
path += `M ${x} ${y}`;
} else {
path += ` L ${x} ${y}`;
}
}
return path;
}
// 生成线芯分叉路径
function generateWireSplitPath(wireIndex, svgWidth, svgHeight, isStart = true, centerYOffset = 0, twistPitchScale = 1) {
const centerY = svgHeight * 0.1 + centerYOffset; // 与双绞线中心位置保持一致,可偏移
const amplitude = 15; // 与双绞线振幅保持一致
const splitOffset = 12; // 减少分叉偏移距离
// 计算双绞线在端点的Y坐标
const canvasLengthMm = 30;
const actualTwistPitch = params.twistPitch * (twistPitchScale || 1);
// 如果绞距为0使用平行线的连接逻辑
if (actualTwistPitch === 0) {
const parallelOffset = 3; // 平行线之间的垂直间距
const wireY = centerY + (wireIndex === 0 ? -parallelOffset : parallelOffset);
if (isStart) {
// 左侧分叉 - 从分离状态连接到平行线起点
const startX = 30;
const endX = 60;
const startY = centerY + (wireIndex === 0 ? -splitOffset : splitOffset);
return `M ${startX} ${startY} L ${endX} ${wireY}`;
} else {
// 右侧分叉 - 从平行线终点连接到分离状态
const startX = svgWidth - 60;
const endX = svgWidth - 30;
const endY = centerY + (wireIndex === 0 ? -splitOffset : splitOffset);
return `M ${startX} ${wireY} L ${endX} ${endY}`;
}
}
let twistCycles = canvasLengthMm / actualTwistPitch;
if (twistCycles > 20) twistCycles = Math.min(twistCycles, 20);
else if (twistCycles < 1) twistCycles = Math.max(twistCycles, 1);
if (isStart) {
// 左侧分叉 - 从分离状态连接到双绞线起点
const startX = 30; // 缩短分叉长度
const endX = 60;
const startY = centerY + (wireIndex === 0 ? -splitOffset : splitOffset);
// 双绞线起点的Y坐标
const angle = wireIndex * Math.PI;
const endY = centerY + amplitude * Math.sin(angle);
return `M ${startX} ${startY} L ${endX} ${endY}`;
} else {
// 右侧分叉 - 从双绞线终点连接到分离状态
const startX = svgWidth - 60;
const endX = svgWidth - 30; // 缩短分叉长度
// 双绞线终点的Y坐标
const frequency = (twistCycles * 2 * Math.PI) / (svgWidth - 120);
const totalLength = svgWidth - 120;
const angle = frequency * totalLength + wireIndex * Math.PI;
const startY = centerY + amplitude * Math.sin(angle);
const endY = centerY + (wireIndex === 0 ? -splitOffset : splitOffset);
return `M ${startX} ${startY} L ${endX} ${endY}`;
}
}
// 生成横截面SVG组件
function generateCrossSectionSVG() {
const svgxSize = 150;
const svgySize = 250;
const centerX = svgxSize / 2;
const centerY = svgySize / 2;
const scale = 60;
const coreRadius = (params.coreDiameter / 2) * scale;
const insulationThickness = params.insulationThickness * scale;
const wireRadius = coreRadius + insulationThickness;
const conductorMat = conductorMaterials.find(m => m.value === params.conductorMaterial);
const insulationMat = insulationMaterials.find(m => m.value === params.insulationMaterial);
// 两根导线的位置(上下紧密接触)
const positions = [
{ x: centerX, y: centerY - wireRadius },
{ x: centerX, y: centerY + wireRadius }
];
const coreColors = [conductorMat?.color || '#CD7F32', '#FF6B35'];
const insulationColors = [insulationMat?.color || '#FF8C42', insulationMat?.color || '#FFB366'];
return (
<svg
viewBox={`0 0 ${svgxSize} ${svgySize}`}
className="w-full h-full"
style={{ minHeight: '100px', minWidth: '100px' }}
>
{/* 背景 */}
<rect width="100%" height="90%" fill="transparent" />
{/* 绘制两根导线 */}
{positions.map((pos, index) => (
<g key={index}>
{/* 绝缘层(外圆) */}
<circle
cx={pos.x}
cy={pos.y}
r={wireRadius}
fill={insulationColors[index]}
stroke="#333"
strokeWidth="1"
style={{
filter: 'drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.1))'
}}
/>
{/* 导体线芯(内圆) */}
<circle
cx={pos.x}
cy={pos.y}
r={coreRadius}
fill={coreColors[index]}
stroke="#333"
strokeWidth="1"
style={{
filter: 'drop-shadow(0.5px 0.5px 1px rgba(0, 0, 0, 0.2))'
}}
/>
</g>
))}
{/* 尺寸标注 */}
</svg>
);
}
return (
<div className="w-full h-full flex">
{/* 左侧参数控制面板 - 20% */}
<div className="w-2/10 p-4 border-r border-[#0ff]/20 overflow-y-auto no-scrollbar h-full text-[#0ff]">
<div className="grid grid-cols-3 grid-rows-3 gap-3">
{/* 第一行:线缆类别、接续方式、占位 */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">线缆类别</label>
<select
value={params.cableType}
onChange={(e) => setParams({ ...params, cableType: e.target.value })}
className="w-full p-2 border rounded text-xs bg-[#1E293B] text-[#0ff] border-[#0ff]/30 focus:border-[#0ff] focus:outline-none"
>
{/* <option value="coaxial">同轴线</option> */}
<option value="twisted_single">双绞线单对</option>
<option value="twisted_dual">双绞线两对</option>
{/* <option value="fiber">光纤</option> */}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">接续方式</label>
<select
value={params.spliceMethod}
onChange={(e) => setParams({ ...params, spliceMethod: e.target.value })}
className="w-full p-2 border rounded text-xs bg-[#1E293B] text-[#0ff] border-[#0ff]/30 focus:border-[#0ff] focus:outline-none"
>
<option value="none"></option>
</select>
</div>
{/* 占位取消注释 */}
{/* <div aria-hidden="true" /> */}
{/* 导体材质(仅双绞线显示) */}
{(params.cableType === 'twisted_single' || params.cableType === 'twisted_dual') && (
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">导体材质</label>
<select
value={params.conductorMaterial}
onChange={(e) => setParams({ ...params, conductorMaterial: e.target.value })}
className="w-full p-2 border rounded text-xs bg-[#1E293B] text-[#0ff] border-[#0ff]/30 focus:border-[#0ff] focus:outline-none"
>
<option value="copper"></option>
<option value="aluminum"></option>
<option value="silver"></option>
</select>
</div>
)}
{/* 线芯直径AWG下拉 */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">线芯直径</label>
<select
value={params.coreDiameter}
onChange={(e) => setParams({ ...params, coreDiameter: parseFloat(e.target.value) })}
className="w-full p-2 border rounded text-xs bg-[#1E293B] text-[#0ff] border-[#0ff]/30 focus:border-[#0ff] focus:outline-none"
>
{awgDropdownOptions.map(opt => (
<option key={opt.label} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
{/* 绝缘材料PP/PE */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">绝缘材料</label>
<select
value={params.insulationMaterial}
onChange={(e) => setParams({ ...params, insulationMaterial: e.target.value })}
className="w-full p-2 border rounded text-xs bg-[#1E293B] text-[#0ff] border-[#0ff]/30 focus:border-[#0ff] focus:outline-none"
>
<option value="pp">PP</option>
<option value="pe">PE</option>
</select>
</div>
{/* 绝缘厚度(滑块 0.1~0.5mm */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">
绝缘厚度:
<span className="block text-[#0ff] font-bold">{params.insulationThickness}mm</span>
</label>
<input
type="range"
min={0.1}
max={0.5}
step={0.01}
value={params.insulationThickness}
onChange={(e) => setParams({ ...params, insulationThickness: parseFloat(e.target.value) })}
className="w-full h-2 bg-[#0ff]/20 rounded appearance-none cursor-pointer"
style={{ accentColor: '#0ff' }}
/>
</div>
{/* 绞距(滑块 5~20mm */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">
线缆绞距:
<span className="block text-[#0ff] font-bold">{params.twistPitch}mm</span>
</label>
<input
type="range"
min={0}
max={20}
step={1}
value={params.twistPitch}
onChange={(e) => setParams({ ...params, twistPitch: parseFloat(e.target.value) })}
className="w-full h-2 bg-[#0ff]/20 rounded appearance-none cursor-pointer"
style={{ accentColor: '#0ff' }}
/>
</div>
{/* 第二对绞距比例(滑块 0.5~2.0,仅两对时显示) */}
{params.cableType === 'twisted_dual' && (
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">
节距比例:
<span className="block text-[#0ff] font-bold">{params.pair2TwistRatio.toFixed(2)}×</span>
</label>
<input
type="range"
min={1}
max={2.5}
step={0.05}
value={params.pair2TwistRatio}
onChange={(e) => setParams({ ...params, pair2TwistRatio: parseFloat(e.target.value) })}
className="w-full h-2 bg-[#0ff]/20 rounded appearance-none cursor-pointer"
style={{ accentColor: '#0ff' }}
/>
</div>
)}
{/* 线缆长度(滑块 1~305m */}
<div>
<label className="block text-xs font-semibold text-[#0ff] mb-1">
线缆长度:
<span className="block text-[#0ff] font-bold">{params.cableLength}m</span>
</label>
<input
type="range"
min={1}
max={305}
step={1}
value={params.cableLength}
onChange={(e) => setParams({ ...params, cableLength: parseInt(e.target.value) })}
className="w-full h-2 bg-[#0ff]/20 rounded appearance-none cursor-pointer"
style={{ accentColor: '#0ff' }}
/>
</div>
</div>
</div>
{/* 中间双绞线可视化 - 60% */}
<div className="w-7/10 p-0 flex flex-col h-full">
{/* 双绞线示意图 - 100% */}
<div className="h-full rounded-xl shadow-2xl p-2 overflow-hidden border mb-0">
<div className="w-full h-full flex flex-col">
<div className="flex-1 relative flex items-center justify-center min-h-0">
<svg
viewBox="0 0 800 150"
className="w-full h-full rounded-lg"
style={{ minHeight: '200px' }}
preserveAspectRatio="xMidYMid meet"
>
{/* 背景网格 */}
<defs>
{/* <pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#f0f0f0" strokeWidth="1"/>
</pattern> */}
{/* 橙白相间条纹图案 */}
<pattern id="orangeWhiteStripes" width="8" height="8" patternUnits="userSpaceOnUse">
<rect width="4" height="8" fill="#ff6600"/>
<rect x="4" width="4" height="8" fill="#ffffff"/>
</pattern>
{/* 蓝白相间条纹图案 */}
<pattern id="blueWhiteStripes" width="8" height="8" patternUnits="userSpaceOnUse">
<rect width="4" height="8" fill="#0066ff"/>
<rect x="4" width="4" height="8" fill="#ffffff"/>
</pattern>
{/* 箭头标记 */}
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#666" />
</marker>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
{/* 左侧分叉 */}
<path
d={leftSplitPath0}
fill="none"
stroke="#ff6600"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={leftSplitPath1}
fill="none"
stroke="url(#orangeWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
{/* 双绞线路径 */}
<path
d={twistedPath0}
fill="none"
stroke="#ff6600"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={twistedPath1}
fill="none"
stroke="url(#orangeWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
{/* 右侧分叉 */}
<path
d={rightSplitPath0}
fill="none"
stroke="#ff6600"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={rightSplitPath1}
fill="none"
stroke="url(#orangeWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
{/* 第二条双绞线(当选择两对时渲染) */}
{params.cableType === 'twisted_dual' && (
<>
{/* 左侧分叉 - 第二对 */}
<path
d={leftSplitPath0_2}
fill="none"
stroke="#0066ff"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={leftSplitPath1_2}
fill="none"
stroke="url(#blueWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
{/* 双绞线路径 - 第二对 */}
<path
d={twistedPath0_2}
fill="none"
stroke="#0066ff"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={twistedPath1_2}
fill="none"
stroke="url(#blueWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
{/* 右侧分叉 - 第二对 */}
<path
d={rightSplitPath0_2}
fill="none"
stroke="#0066ff"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
<path
d={rightSplitPath1_2}
fill="none"
stroke="url(#blueWhiteStripes)"
strokeWidth={Math.max(3, params.coreDiameter * 8)}
strokeLinecap="round"
style={{
filter: 'drop-shadow(2px 2px 4px rgba(255, 102, 0, 0.3))'
}}
/>
</>
)}
</svg>
{/* 隐藏测试端口标记左右分叉两头共4处 */}
{/* 左侧分叉外端两个点(动态计算) */}
<div id="pair1-line1-left" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions.leftTop.leftPct}%`, top: `${splitMarkerPositions.leftTop.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
<div id="pair1-line2-left" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions.leftBottom.leftPct}%`, top: `${splitMarkerPositions.leftBottom.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
{/* 右侧分叉外端两个点(动态计算) */}
<div id="pair1-line1-right" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions.rightTop.leftPct}%`, top: `${splitMarkerPositions.rightTop.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
<div id="pair1-line2-right" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions.rightBottom.leftPct}%`, top: `${splitMarkerPositions.rightBottom.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
{params.cableType === 'twisted_dual' && (
<>
{/* 第二对隐藏测试端口标记 */}
<div id="pair2-line1-left" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions2.leftTop.leftPct}%`, top: `${splitMarkerPositions2.leftTop.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
<div id="pair2-line2-left" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions2.leftBottom.leftPct}%`, top: `${splitMarkerPositions2.leftBottom.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
<div id="pair2-line1-right" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions2.rightTop.leftPct}%`, top: `${splitMarkerPositions2.rightTop.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
<div id="pair2-line2-right" jstype="cal-copper-out" className="absolute" style={{ left: `${splitMarkerPositions2.rightBottom.leftPct}%`, top: `${splitMarkerPositions2.rightBottom.topPct}%`, width: '0px', height: '0px', opacity: 0 }} />
</>
)}
</div>
</div>
</div>
</div>
{/* 右侧横截面和电气参数 - 20% */}
<div className="w-1/10 p-0 flex flex-col h-full">
{/* <div className="mb-6">
<h3 className="text-xl font-bold text-gray-800 mb-2">横截面图</h3>
<div className="w-12 h-1 bg-orange-500 rounded"></div>
</div> */}
<div className="w-full ">
{crossSectionSVG}
</div>
</div>
</div>
);
};
export default CopperAnalyzer;

View File

@@ -0,0 +1,98 @@
import React from 'react';
// 纯文字版任务描述插入损耗IL与回波损耗RL系列实验
export default function CopperAnalyzerTask() {
return (
<div className="bg-gradient-to-br from-[#1E293B] to-[#0F172A] p-6 rounded-lg w-full h-[80vh] overflow-y-auto no-scrollbar flex flex-col gap-6 shadow-2xl border border-[#334155]">
<div className="border-b border-cyan-700 pb-3 mb-3">
<p className="text-sm font-semibold text-cyan-400 uppercase tracking-wider">实验任务页</p>
<h1 className="text-2xl md:text-3xl font-bold text-gray-100 mt-1">铜缆分析任务插入损耗与回波损耗</h1>
<p className="text-xs text-gray-400">本任务通过测量铜缆在不同参数下的插入损耗IL与回波损耗RL来分析影响信号衰减与反射的关键物理参数及其随频率的变化规律</p>
</div>
<section className="space-y-2">
<h2 className="text-lg font-semibold text-cyan-300">任务概述</h2>
<ul className="list-disc list-inside text-gray-300 space-y-1">
<li>主题铜缆的插入损耗IL与回波损耗RL实验</li>
<li>目的理解影响信号衰减与反射的关键物理参数及其随频率的变化规律</li>
<li>记录项导体材质 / 线径 / 长度 / 频点 / IL(dB) / RL(dB) / Z0(Ω)</li>
</ul>
</section>
<section className="space-y-2">
<h2 className="text-lg font-semibold text-cyan-300">通用准备A0/B0</h2>
<ul className="list-disc list-inside text-gray-300 space-y-1">
<li>对照默认值材质铜AWG24(0.511mm)PE厚度0.2mm绞距12mm长度20m频点1/10/100MHz</li>
<li>原则每次只改变一个变量其余保持不变</li>
<li>记录项材质 / 线径 / 长度 / 频点 / IL(dB) / RL(dB) / Z0(Ω)</li>
</ul>
</section>
<section className="space-y-2">
<h2 className="text-lg font-semibold text-cyan-300">A. 插入损耗Insertion Loss系列实验</h2>
<div>
<h3 className="text-cyan-200 font-semibold">A1 长度对插入损耗的影响</h3>
<p className="text-gray-300 text-sm">固定对照参数长度依次1/10/50/100m记录 1/10/100 MHz 的插损</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>插损随长度近似线性增加同频点下 10m 1m 10 </li>
<li>曲线随频率上升整体上移高频衰减更严重</li>
<li>讨论为何工程上存在 100m 长度限制</li>
</ul>
</div>
<div>
<h3 className="text-cyan-200 font-semibold">A2 线芯直径对插入损耗的影响</h3>
<p className="text-gray-300 text-sm">AWG 26/24/22固定其它参数 1/10/100 MHz 记录插损</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>直径越粗插损越小电阻更低</li>
<li>高频差异更明显趋肤效应</li>
<li>讨论为何粗导线电阻小趋肤效应如何影响</li>
</ul>
</div>
<div>
<h3 className="text-cyan-200 font-semibold">A3 导体材质对插入损耗的影响</h3>
<p className="text-gray-300 text-sm">材质//固定其它参数在三频点记录插损</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>插损趋势 &lt; &lt; 银最好铜次之铝衰减最大</li>
<li>讨论为何实际多用铜而非银成本/加工/氧化</li>
</ul>
</div>
<div>
<h3 className="text-cyan-200 font-semibold">A4 频率对插入损耗的影响观察频谱</h3>
<p className="text-gray-300 text-sm">固定对照参数绘制或读取 1100 MHz IL 曲线或在指定频点记录值</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>插损随频率单调增加曲线平滑上升高频损耗更大</li>
<li>讨论为什么频率越高损耗越大趋肤效应 + 介质损耗</li>
</ul>
</div>
</section>
<section className="space-y-2">
<h2 className="text-lg font-semibold text-cyan-300">B. 回波损耗Return Loss系列实验</h2>
<div>
<h3 className="text-cyan-200 font-semibold">B1 阻抗偏离对回波损耗的影响</h3>
<p className="text-gray-300 text-sm">先观察对照组 Z0修改参数使 Z0 逐步偏离 100Ω分别记录特性阻抗与 1/10/100 MHz RL</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>Z0 越接近 100ΩRL 越高偏离越多RL 下降</li>
<li>讨论RL 低时通信的影响反射增大眼图闭合误码率上升</li>
</ul>
</div>
<div>
<h3 className="text-cyan-200 font-semibold">B2 绝缘材料/厚度对回波损耗的影响</h3>
<p className="text-gray-300 text-sm">固定其余参数依次改变绝缘材料PE / PVC / PTFE或绝缘厚度0.10.5mm记录 Z0 与各频点 RL</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>εr 越高或绝缘越薄 Z0 越低 若远离 100Ω RL 变差</li>
<li>讨论为什么绝缘材料的 εr 会影响阻抗电容变化影响 Z0</li>
</ul>
</div>
<div>
<h3 className="text-cyan-200 font-semibold">B3 TDR 波形观察</h3>
<p className="text-gray-300 text-sm">观察不同参考阻抗下变化 TDR 曲线的趋势定位不匹配位置</p>
<ul className="list-disc list-inside text-[#0ff]/80 text-sm mt-1">
<li>阻抗偏离越大TDR 曲线振幅越大</li>
<li>讨论如何根据 TDR 波形定位阻抗不匹配的位置</li>
</ul>
</div>
</section>
</div>
);
}

View File

@@ -6,6 +6,7 @@ import useDisplayStore from '@/store/displayStore';
import ContextMenu from '@/components/ContextMenu';
import JsPlumbInit from '@/components/JsPlumbInit';
import ConnectionAnalyzer from '@/components/ConnectionAnalyzer';
import CopperAnalyzer from '@/components/scene/CopperAnalyzer';
import Office from '@/components/scene/Office';
import Industry from '@/components/scene/Industry';
import DataCenter from '@/components/scene/DataCenter';
@@ -15,6 +16,7 @@ import OfficeTask from '@/components/scene/OfficeTask';
import DataCenterTask from '@/components/scene/DataCenterTask';
import IndustryTask from '@/components/scene/IndustryTask';
import WorldSkillTask from '@/components/scene/WorldSkillTask';
import CopperAnalyzerTask from '@/components/scene/CopperAnalyzerTask';
import DisPlay from '@/components/DisPlay';
import Cursors from '@/components/Cursors';
import SourceCheck from '@/components/SourceCheck';
@@ -60,6 +62,9 @@ export default function Home() {
const {
devices, // 所有设备列表
installedDevices, // 已安装的设备
installDevice, // 安装设备的方法
uninstallDevice, // 卸载设备的方法
mainUnitModules, // 主机已安装的模块
remoteUnitModules, // 远端已安装的模块
mainUnitAdapter, // 主机已安装的适配器
@@ -95,9 +100,34 @@ export default function Home() {
const [isFromUrl, setIsFromUrl] = useState(false);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [confirmDialogMessage, setConfirmDialogMessage] = useState('');
const [selectedTabIndex, setSelectedTabIndex] = useState(0); // 配件区域选中的标签页索引
const [confirmDialogCallback, setConfirmDialogCallback] = useState(null);
const { play } = useAudio();
// 监听设备变化,当设备被移除时自动切换回设备标签页
useEffect(() => {
const mainDevice = installedDevices.main;
let allowedCategories = ['设备']; // 默认显示设备
if (mainDevice) {
if (mainDevice.id === 'dsx') {
allowedCategories = ['设备', '模块', '适配器', '跳线', '连接器', '工具'];
} else if (mainDevice.id === 'est-analyzer') {
allowedCategories = ['设备', '夹具', '基准'];
} else if (mainDevice.id === 'wifi') {
allowedCategories = ['设备'];
}
}
// 获取当前可用的设备类别
const availableCategories = Object.keys(devices).filter(category => allowedCategories.includes(category));
// 如果当前选中的标签页索引超出了可用范围则重置为0设备标签页
if (selectedTabIndex >= availableCategories.length) {
setSelectedTabIndex(0);
}
}, [installedDevices.main, selectedTabIndex, devices]);
// iframe内嵌检测
useEffect(() => {
@@ -288,10 +318,39 @@ function classNames(...classes) {
const item = JSON.parse(e.dataTransfer.getData('text/plain'));
// 处理设备的安装
if (item.type === 'device') {
const currentDevice = installedDevices[target];
// 检查是否已安装设备
if (currentDevice) {
if (currentDevice.id === item.id) {
// alert('该设备已经安装,不能重复安装!');
} else {
// alert('该位置已安装了设备,请先卸载后再安装新设备!');
}
return;
}
installDevice(item, target);
}
// 处理模块的安装
if (item.type === 'module') {
else if (item.type === 'module') {
const currentDevice = installedDevices[target];
const targetModules = target === 'main' ? mainUnitModules : remoteUnitModules;
// 检查是否已安装设备
if (!currentDevice) {
// alert('请先安装设备,再安装模块!');
return;
}
// 检查设备是否为dsx
if (currentDevice.id !== 'dsx' && currentDevice.id !== 'est-analyzer') {
// alert('模块只能安装到DSX认证测试仪或EST分析仪设备上');
return;
}
// 检查是否已安装模块
if (targetModules.length > 0) {
// alert('该设备已安装了测试模块,请先卸载后再安装新模块!');
@@ -304,9 +363,22 @@ function classNames(...classes) {
}
// 处理适配器的安装
else if (item.type === 'adapter') {
const currentDevice = installedDevices[target];
const targetModules = target === 'main' ? mainUnitModules : remoteUnitModules;
const targetAdapter = target === 'main' ? mainUnitAdapter : remoteUnitAdapter;
// 检查是否已安装设备
if (!currentDevice) {
// alert('请先安装设备,再安装适配器!');
return;
}
// 检查设备是否为dsx
if (currentDevice.id !== 'dsx') {
// alert('适配器只能安装到DSX认证测试仪设备上');
return;
}
// 检查是否正确安装模块
if (!targetModules.some(m => m.id === '8000')) {
// alert('请先安装铜缆测试模块,再安装适配器!');
@@ -359,7 +431,7 @@ function classNames(...classes) {
// 检查是否安装了OTDR模块用于控制远端区域的显示
const hasOtdrModule = mainUnitModules.some(m => m.id === 'ofp');
const hasWiFiModule = installedDevices.main && installedDevices.main.id === 'wifi';
//鼠标元素穿透检测显示到鼠标元素穿透检测显示到StatusToast
const [portTooltip, setPortTooltip] = useState('');
@@ -439,14 +511,14 @@ function classNames(...classes) {
<>
{/* 开发环境切换型号按钮 */}
{/* <div className="fixed bottom-4 right-4 z-50">
<button
onClick={() => updateEstmodel(estmodel === 'fluke' ? 'general' : 'fluke')}
className="px-3 py-2 text-sm rounded bg-blue-600 text-white hover:bg-blue-700"
>
切换型号:{estmodel}
</button>
</div> */}
{/* <div className="fixed bottom-4 right-4 z-50">
<button
onClick={() => updateEstmodel(estmodel === 'fluke' ? 'general' : 'fluke')}
className="px-3 py-2 text-sm rounded bg-blue-600 text-white hover:bg-blue-700"
>
切换型号:{estmodel}
</button>
</div> */}
<style>{scrollbarStyles}</style>
{/* 比赛ID */}
@@ -661,6 +733,7 @@ function classNames(...classes) {
faultScenarios === 'Industry' ? <IndustryTask /> :
faultScenarios === 'DataCenter' ? <DataCenterTask /> :
faultScenarios === 'WorldSkill' ? <WorldSkillTask /> :
faultScenarios === 'CopperAnalyzer' ? <CopperAnalyzerTask /> :
<WorldSkillTask />}
</div>
@@ -684,7 +757,9 @@ function classNames(...classes) {
<div className="h-screen bg-[#0F172A] p-6 overflow-hidden">
{/* 故障箱区域 */}
<div className="h-[30vh] bg-[#0F172A] shadow-lg p-2 mb-4 border border-[#0ff]/20 rounded-lg">
{faultScenarios === 'Office' ? <Office key={useDeviceStore.getState().reloadKey} /> :
{
faultScenarios === 'CopperAnalyzer' ? <CopperAnalyzer /> :
faultScenarios === 'Office' ? <Office key={useDeviceStore.getState().reloadKey} /> :
faultScenarios === 'Industry' ? <Industry key={useDeviceStore.getState().reloadKey} /> :
faultScenarios === 'DataCenter' ? <DataCenter key={useDeviceStore.getState().reloadKey} /> :
faultScenarios === 'WorldSkill' ? (
@@ -1067,19 +1142,39 @@ function classNames(...classes) {
</p>
</div>
{/* 隐藏的左侧端点元素 */}
{item.portType === 'cal-copper-out' ? (
<div
id={`benchmark-${index + 1}-left`}
jstype={item.portType}
lcclean={item.portType === 'fiber' ? 'false' : undefined}
className="absolute -left-4 -bottom-4 w-6 h-6 opacity-0"
/>
) : (
<div
id={`${item.id}-${index + 1}-left`}
jstype={`cable-${item.portType}-left`}
lcclean={item.portType === 'fiber' ? 'false' : undefined}
className="absolute -left-4 -bottom-4 w-6 h-6 opacity-0"
/>
{/* 隐藏的右侧端点元素 */}
)}
{/* 隐藏的右侧端点元素 */}
{item.portType === 'cal-copper-out' ? (
<div
id={`benchmark-${index + 1}-right`}
jstype={item.portType}
lcclean={item.portType === 'fiber' ? 'false' : undefined}
className="absolute -right-4 -bottom-4 w-6 h-6 opacity-0"
/>
) : (
<div
id={`${item.id}-${index + 1}-right`}
jstype={`cable-${item.portType}-right`}
lcclean={item.portType === 'fiber' ? 'false' : undefined}
className="absolute -right-4 -top-4 w-6 h-6 opacity-0"
/>
)}
</div>
))}
</div>
@@ -1116,62 +1211,78 @@ function classNames(...classes) {
}
}}
>
{/* 设备安装容器 */}
<div className="relative w-[50%] top-8 aspect-[2/3] mt-8">
{/* 适配器安装框 */}
{mainUnitModules.some(m => m.id === '8000') && (
<div className="absolute -top-14 left-1/2 transform -translate-x-1/2 w-28 h-20 border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
{mainUnitAdapter ? (
<div className="relative w-full h-full">
<div
className="relative w-full h-full"
onContextMenu={(e) => handleContextMenu(e, 'main', { type: 'adapter' })}
>
<Image
src={getAssetUrl(mainUnitAdapter.image)}
alt={mainUnitAdapter.name}
fill
sizes={"auto"}
className="object-contain p-1"
/>
</div>
{/* 适配器顶部的连线框 */}
<div
className="absolute -top-0 h-10 rounded-lg group"
id={`main-${mainUnitAdapter.port.id}`}
jstype={`${mainUnitAdapter.jstype}`}
>
</div>
{installedDevices.main ? (
<div
className="relative w-full h-full"
onContextMenu={(e) => {
e.preventDefault();
e.stopPropagation();
handleContextMenu(e, 'main', { type: 'device', device: installedDevices.main });
}}
>
{/* 适配器安装框 */}
{mainUnitModules.some(m => m.id === '8000') && (
<div className="absolute -top-14 left-1/2 transform -translate-x-1/2 w-28 h-20 border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
{mainUnitAdapter ? (
<div className="relative w-full h-full">
<div
className="relative w-full h-full"
onContextMenu={(e) => handleContextMenu(e, 'main', { type: 'adapter' })}
>
<Image
src={getAssetUrl(mainUnitAdapter.image)}
alt={mainUnitAdapter.name}
fill
sizes={"auto"}
className="object-contain p-1"
/>
</div>
{/* 适配器顶部的连线框 */}
<div
className="absolute -top-0 h-10 rounded-lg group"
id={`main-${mainUnitAdapter.port.id}`}
jstype={`${mainUnitAdapter.jstype}`}
>
</div>
</div>
) : (
<span className="text-[#0ff] text-xs">适配器插槽</span>
)}
</div>
) : (
<span className="text-[#0ff] text-xs">适配器插槽</span>
)}
</div>
)}
{/* 接口框 */}
{mainUnitPorts.length > 0 && (
<div className="absolute -top-7 left-0 right-0 flex justify-center gap-3">
{mainUnitPorts.map((port, index,) => (
<div
key={index}
id={`main-${port.id}`}
jstype={`modelport-${port.module === '8000' ? 'copper' : 'fiber'}`}
className="w-6 h-8 border-2 border-dashed border-[#0ff]/30 rounded-lg relative group"
lcclean={['cfp', 'ofp'].includes(port.module) ? 'false' : undefined}
>
{/* 接口框 */}
{mainUnitPorts.length > 0 && (
<div className="absolute -top-7 left-0 right-0 flex justify-center gap-3">
{mainUnitPorts.map((port, index,) => (
<div
key={index}
id={`main-${port.id}`}
jstype={port.type === 'cal-copper' ? 'cal-copper' : `modelport-${port.module === '8000' ? 'copper' : 'fiber'}`}
className="w-6 h-8 border-2 border-dashed border-[#0ff]/30 rounded-lg relative group"
lcclean={['cfp', 'ofp'].includes(port.module) ? 'false' : undefined}
>
</div>
))}
</div>
))}
)}
<Image
src={getAssetUrl(installedDevices.main.image)}
alt={installedDevices.main.name}
sizes={"auto"}
fill
className="object-contain"
priority
/>
</div>
) : (
<div className="w-full h-full border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
<span className="text-[#0ff] text-sm">拖拽设备到此处安装</span>
</div>
)}
<Image
src={getAssetUrl("/DSX-MA.png")}
alt="DSX 主机"
sizes={"auto"}
fill
className="object-contain"
priority
/>
</div>
{/* 已安装模块列表 */}
@@ -1191,7 +1302,7 @@ function classNames(...classes) {
</div>
{/* 远端区域 */}
{!hasOtdrModule ? (
{!hasOtdrModule && !hasWiFiModule ? (
<div className="w-[calc(50%-0.5rem)] bg-[#0F172A] shadow-lg p-4 border border-[#0ff]/20 rounded-lg">
<div
className="rounded-lg h-[calc(100%-3rem)] p-4 flex flex-col items-center justify-center relative"
@@ -1217,62 +1328,78 @@ function classNames(...classes) {
}
}}
>
{/* 设备安装容器 */}
<div className="relative w-[50%] top-8 aspect-[2/3] mt-8">
{/* 远端适配器安装框 */}
{remoteUnitModules.some(m => m.id === '8000') && (
<div className="absolute -top-14 left-1/2 transform -translate-x-1/2 w-28 h-20 border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
{remoteUnitAdapter ? (
<div className="relative w-full h-full">
<div
className="relative w-full h-full"
onContextMenu={(e) => handleContextMenu(e, 'remote', { type: 'adapter' })}
>
<Image
src={getAssetUrl(remoteUnitAdapter.image)}
alt={remoteUnitAdapter.name}
sizes={"auto"}
fill
className="object-contain p-1"
/>
</div>
{/* 适配器顶部的连线框 */}
<div
className="absolute -top-0 h-10 rounded-lg group"
id={`remote-${remoteUnitAdapter.port.id}`}
jstype={`${remoteUnitAdapter.jstype}`}
>
</div>
{installedDevices.remote ? (
<div
className="relative w-full h-full"
onContextMenu={(e) => {
e.preventDefault();
e.stopPropagation();
handleContextMenu(e, 'remote', { type: 'device', device: installedDevices.remote });
}}
>
{/* 远端适配器安装框 */}
{remoteUnitModules.some(m => m.id === '8000') && (
<div className="absolute -top-14 left-1/2 transform -translate-x-1/2 w-28 h-20 border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
{remoteUnitAdapter ? (
<div className="relative w-full h-full">
<div
className="relative w-full h-full"
onContextMenu={(e) => handleContextMenu(e, 'remote', { type: 'adapter' })}
>
<Image
src={getAssetUrl(remoteUnitAdapter.image)}
alt={remoteUnitAdapter.name}
sizes={"auto"}
fill
className="object-contain p-1"
/>
</div>
{/* 适配器顶部的连线框 */}
<div
className="absolute -top-0 h-10 rounded-lg group"
id={`remote-${remoteUnitAdapter.port.id}`}
jstype={`${remoteUnitAdapter.jstype}`}
>
</div>
</div>
) : (
<span className="text-[#0ff] text-xs">适配器插槽</span>
)}
</div>
) : (
<span className="text-[#0ff] text-xs">适配器插槽</span>
)}
</div>
)}
{/* 远端接口框 */}
{remoteUnitPorts.length > 0 && (
<div className="absolute -top-7 left-0 right-0 flex justify-center gap-3">
{remoteUnitPorts.map((port, index) => (
<div
key={index}
id={`remote-${port.id}`}
jstype={`modelport-${port.module === '8000' ? 'copper' : 'fiber'}`}
className="w-6 h-8 border-2 border-dashed border-[#0ff]/30 rounded-lg relative group"
lcclean={['cfp', 'ofp'].includes(port.module) ? 'false' : undefined}
>
{/* 远端接口框 */}
{remoteUnitPorts.length > 0 && (
<div className="absolute -top-7 left-0 right-0 flex justify-center gap-3">
{remoteUnitPorts.map((port, index) => (
<div
key={index}
id={`remote-${port.id}`}
jstype={port.type === 'cal-copper' ? 'cal-copper' : `modelport-${port.module === '8000' ? 'copper' : 'fiber'}`}
className="w-6 h-8 border-2 border-dashed border-[#0ff]/30 rounded-lg relative group"
lcclean={['cfp', 'ofp'].includes(port.module) ? 'false' : undefined}
>
</div>
))}
</div>
))}
)}
<Image
src={getAssetUrl(installedDevices.remote.image)}
alt={installedDevices.remote.name}
sizes={"auto"}
fill
className="object-contain"
priority
/>
</div>
) : (
<div className="w-full h-full border-2 border-dashed border-[#0ff]/30 rounded-lg flex items-center justify-center">
<span className="text-[#0ff] text-sm">拖拽设备到此处安装</span>
</div>
)}
<Image
src={getAssetUrl("/DSX-RE.png")}
alt="DSX 远端"
sizes={"auto"}
fill
className="object-contain"
priority
/>
</div>
{/* 已安装模块列表 */}
@@ -1294,76 +1421,119 @@ function classNames(...classes) {
<div className="w-[calc(50%-0.5rem)]"></div>
)}
</div>
</div>
{/* 配件区域 */}
<div className="w-[23%] bg-[#0F172A] shadow-lg p-4 border border-[#0ff]/20 flex flex-col h-full rounded-lg">
<div className="flex flex-col flex-1">
<Tab.Group as="div" className="flex flex-col flex-1">
<Tab.Group as="div" className="flex flex-col flex-1" selectedIndex={selectedTabIndex} onChange={setSelectedTabIndex}>
<Tab.List className="flex space-x-1 rounded-xl bg-[#0ff]/10 p-1 flex-shrink-0">
{Object.keys(devices).map((category) => (
<Tab
key={category}
className={({ selected }) =>
classNames(
'w-full rounded-lg py-2.5 text-sm font-medium leading-5',
'ring-[#0ff] ring-opacity-60 ring-offset-2 ring-offset-[#0F172A] focus:outline-none focus:ring-2',
selected
? 'bg-[#0F172A] shadow text-[#00ff7f]'
: 'text-[#0ff] hover:bg-[#0ff]/[0.12] hover:text-[#00ff7f]'
)
{(() => {
// 根据主机安装的设备类型过滤显示的设备类别
const mainDevice = installedDevices.main;
let allowedCategories = ['设备']; // 默认显示设备
if (mainDevice) {
if (mainDevice.id === 'dsx') {
// 如果安装了dsx显示设备、模块、适配器、跳线、连接器、工具
allowedCategories = ['设备', '模块', '适配器', '跳线', '连接器', '工具'];
} else if (mainDevice.id === 'est-analyzer') {
// 如果安装了est-analyzer显示设备和夹具
allowedCategories = ['设备', '夹具', '基准'];
} else if (mainDevice.id === 'wifi') {
// 如果安装了wifi只显示设备
allowedCategories = ['设备'];
}
>
{category}
</Tab>
))}
}
return Object.keys(devices)
.filter(category => allowedCategories.includes(category))
.map((category) => (
<Tab
key={category}
className={({ selected }) =>
classNames(
'w-full rounded-lg py-2.5 text-sm font-medium leading-5',
'ring-[#0ff] ring-opacity-60 ring-offset-2 ring-offset-[#0F172A] focus:outline-none focus:ring-2',
selected
? 'bg-[#0F172A] shadow text-[#00ff7f]'
: 'text-[#0ff] hover:bg-[#0ff]/[0.12] hover:text-[#00ff7f]'
)
}
>
{category}
</Tab>
));
})()}
</Tab.List>
<Tab.Panels className="flex-1 mt-2 relative">
{Object.entries(devices).map(([category, categoryDevices], idx) => (
<Tab.Panel
key={idx}
className="absolute inset-0 overflow-y-auto custom-scrollbar"
>
<div className="grid grid-cols-2 gap-3 p-2">
{categoryDevices.map((device) => {
const isSelected = category === '工具' && device.id === useDeviceStore.getState().selectedTool?.id;
return (
<div
key={device.id}
className={`bg-[#0F172A] p-2 rounded shadow border ${isSelected ? 'border-[#00ff7f]' : 'border-[#0ff]/20 hover:border-[#0ff]/40'} transition-colors cursor-pointer z-3000`}
draggable={category !== '工具'}
onDragStart={(e) => handleDragStart(e, device)}
onClick={() => {
if (category === '工具') {
if (isSelected) {
useDeviceStore.getState().clearSelectedTool();
} else {
useDeviceStore.getState().selectTool(device);
}
}
}}
>
{device.image && (
<div className="relative w-full aspect-[1/1] mb-2">
<Image
src={getAssetUrl(device.image)}
alt={device.name}
sizes={"auto"}
fill
className="object-contain p-1"
/>
{(() => {
// 根据主机安装的设备类型过滤显示的设备类别
const mainDevice = installedDevices.main;
let allowedCategories = ['设备']; // 默认显示设备
if (mainDevice) {
if (mainDevice.id === 'dsx') {
// 如果安装了dsx显示设备、模块、适配器、跳线、连接器、工具
allowedCategories = ['设备', '模块', '适配器', '跳线', '连接器', '工具'];
} else if (mainDevice.id === 'est-analyzer') {
// 如果安装了est-analyzer显示设备和夹具
allowedCategories = ['设备', '夹具', '基准'];
} else if (mainDevice.id === 'wifi') {
// 如果安装了wifi只显示设备
allowedCategories = ['设备'];
}
}
return Object.entries(devices)
.filter(([category]) => allowedCategories.includes(category))
.map(([category, categoryDevices], idx) => (
<Tab.Panel
key={idx}
className="absolute inset-0 overflow-y-auto custom-scrollbar"
>
<div className="grid grid-cols-2 gap-3 p-2">
{categoryDevices.map((device) => {
const isSelected = category === '工具' && device.id === useDeviceStore.getState().selectedTool?.id;
return (
<div
key={device.id}
className={`bg-[#0F172A] p-2 rounded shadow border ${isSelected ? 'border-[#00ff7f]' : 'border-[#0ff]/20 hover:border-[#0ff]/40'} transition-colors cursor-pointer z-3000`}
draggable={category !== '工具'}
onDragStart={(e) => handleDragStart(e, device)}
onClick={() => {
if (category === '工具') {
if (isSelected) {
useDeviceStore.getState().clearSelectedTool();
} else {
useDeviceStore.getState().selectTool(device);
}
}
}}
>
{device.image && (
<div className="relative w-full aspect-[1/1] mb-2">
<Image
src={getAssetUrl(device.image)}
alt={device.name}
sizes={"auto"}
fill
className="object-contain p-1"
/>
</div>
)}
<p className={`font-medium ${isSelected ? 'text-[#00ff7f]' : 'text-[#0ff]'} text-sm truncate`}>{device.name}</p>
{device.description && (
<p className="text-xs text-[#0ff] mt-1 line-clamp-2">{device.description}</p>
)}
</div>
)}
<p className={`font-medium ${isSelected ? 'text-[#00ff7f]' : 'text-[#0ff]'} text-sm truncate`}>{device.name}</p>
{device.description && (
<p className="text-xs text-[#0ff] mt-1 line-clamp-2">{device.description}</p>
)}
</div>
);
})}
</div>
</Tab.Panel>
))}
);
})}
</div>
</Tab.Panel>
));
})()}
</Tab.Panels>
</Tab.Group>
</div>
@@ -1398,7 +1568,28 @@ function classNames(...classes) {
setContextMenu(null);
}
}
] : contextMenu.item?.type === 'adapter' ? [
] : contextMenu.item?.type === 'device' ? (
((contextMenu.target === 'main' ? mainUnitModules.length : remoteUnitModules.length) > 0)
? [
{
label: '卸载模块',
onClick: () => {
const modules = contextMenu.target === 'main' ? mainUnitModules : remoteUnitModules;
handleUninstall(modules[modules.length - 1].id, contextMenu.target);
setContextMenu(null);
}
}
]
: [
{
label: '卸载设备',
onClick: () => {
uninstallDevice(contextMenu.target);
setContextMenu(null);
}
}
]
) : contextMenu.item?.type === 'adapter' ? [
{
label: '卸载适配器',
onClick: () => {

View File

@@ -3,7 +3,7 @@ import { devtools } from 'zustand/middleware';
const useDeviceStore = create(
// devtools(
devtools(
(set, get) => ({
// 版本控制
estmodel: 'general',
@@ -12,6 +12,29 @@ const useDeviceStore = create(
// 各个类别的设备
devices: {
'设备': [
{
id: 'dsx',
name: '认证测试仪',
type: 'device',
image: '/DSX-MA.png',
description: '认证分析测试仪仪'
},
{
id: 'est-analyzer',
name: '线缆分析仪',
type: 'device',
image: '/cableanalyzer.png',
description: '平衡/不平衡分析仪'
},
{
id: 'wifi',
name: '无线分析仪',
type: 'device',
image: '/wifi.png',
description: '无线信号分析仪'
}
],
'模块': [
{
id: '8000',
@@ -201,6 +224,35 @@ const useDeviceStore = create(
description: '音频探棒'
}
],
'夹具': [
{
id: 'cal-2p',
name: '测试夹具-2芯',
type: 'module',
portType: 'copper',
image: '/CAL-2P.png',
description: '插座测试夹具2芯'
},
{
id: 'cal-4p',
name: '测试夹具-4芯',
type: 'module',
portType: 'copper',
image: '/CAL-4P.png',
description: '插座测试夹具4芯'
}
],
'基准': [
{
id: 'pachcode-copper',
name: '基准连接器',
type: 'cable',
portType: 'cal-copper-out',
image: '/benchmark.png',
description: '基准连接'
},
],
},
@@ -210,6 +262,7 @@ const useDeviceStore = create(
// faultScenarios: "WorldSkill",
// faultScenarios: "Industry",
// faultScenarios: "DataCenter",
// faultScenarios: "CopperAnalyzer",
WorldSkillScenarios: "OFFICE",
// 赛位号
seatNumber: "",
@@ -219,6 +272,8 @@ const useDeviceStore = create(
seatStartTime:null,
// 表单记录
reports:[],
// 当前安装设备 - 格式: { main: device, remote: device }
installedDevices: { main: null, remote: null },
// 主机已安装模块
mainUnitModules: [],
// 远端已安装模块
@@ -239,6 +294,21 @@ const useDeviceStore = create(
connectionPaths: [],
// 连接状态分析结果
connectionStatus: [],
// 线缆参数
cableParams: {
conductorMaterial: 'copper',
coreDiameter: 0.57,
insulationMaterial: 'pe',
insulationThickness: 0.2,
twistPitch: 12, // 绞距 (mm)
pair2TwistRatio: 1.0, // 第二对绞距比例 (相对于第一对)
cableLength: 100, // 线缆长度 (m)
R: 0, // 电阻 R (Ω/m)
L: 0, // 电感 L (nH/m)
C: 0, // 电容 C (pF/m)
G: 0 // 电导 G (nS/m)
},
// 选择工具
selectTool: (tool) => {
@@ -249,6 +319,43 @@ const useDeviceStore = create(
set({ selectedTool: null });
},
// 安装设备
installDevice: (device, target) => {
set((state) => ({
installedDevices: {
...state.installedDevices,
[target]: device
}
}));
},
// 卸载设备
uninstallDevice: (target) => {
set((state) => {
// 卸载设备时,同时清除该设备上的所有模块和适配器
const updates = {
installedDevices: {
...state.installedDevices,
[target]: null
}
};
// 清除对应的模块和适配器
if (target === 'main') {
updates.mainUnitModules = [];
updates.mainUnitAdapter = null;
updates.mainUnitFixture = null;
updates.mainUnitPorts = [];
} else {
updates.remoteUnitModules = [];
updates.remoteUnitAdapter = null;
updates.remoteUnitPorts = [];
}
return updates;
});
},
// 安装模块
installModule: (module, target) => {
set((state) => {
@@ -267,6 +374,18 @@ const useDeviceStore = create(
{ id: 'ofp-mm-out', type: 'modelport', module: 'ofp' },
{ id: 'vfl', type: 'modelport', module: 'ofp' }
];
} else if (module.id === 'cal-2p') {
ports = [
{ id: 'cal-1p-1', type: 'cal-copper', module: 'cal-2p' },
{ id: 'cal-1p-2', type: 'cal-copper', module: 'cal-2p' }
];
} else if (module.id === 'cal-4p') {
ports = [
{ id: 'cal-2p-1', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-2', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-3', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-4', type: 'cal-copper', module: 'cal-4p' }
];
}
return {
@@ -282,6 +401,18 @@ const useDeviceStore = create(
{ id: 'cfp-mm-out', type: 'modelport', module: 'cfp' },
{ id: 'vfl', type: 'modelport', module: 'cfp' }
];
} else if (module.id === 'cal-2p') {
ports = [
{ id: 'cal-1p-1', type: 'cal-copper', module: 'cal-2p' },
{ id: 'cal-1p-2', type: 'cal-copper', module: 'cal-2p' }
];
} else if (module.id === 'cal-4p') {
ports = [
{ id: 'cal-2p-1', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-2', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-3', type: 'cal-copper', module: 'cal-4p' },
{ id: 'cal-2p-4', type: 'cal-copper', module: 'cal-4p' }
];
}
return {
@@ -331,6 +462,7 @@ const useDeviceStore = create(
[target === 'main' ? 'mainUnitAdapter' : 'remoteUnitAdapter']: null
}));
},
// 更新故障场景
updateFaultScenarios: (status) => {
@@ -431,11 +563,51 @@ const useDeviceStore = create(
setShowTotalToast: (show) => set({ showTotalToast: show }),
setTotalToastMessage: (message) => set({ totalToastMessage: message }),
// ---***线缆参数管理***---
// 更新线缆参数
updateCableParams: (params) =>
set((state) => ({
cableParams: {
...state.cableParams,
...params
}
})),
// 更新单个线缆参数
updateSingleCableParam: (key, value) =>
set((state) => ({
cableParams: {
...state.cableParams,
[key]: value
}
})),
// 重置线缆参数
resetCableParams: () =>
set(() => ({
cableParams: {
conductorMaterial: 'copper',
coreDiameter: 0.57,
insulationMaterial: 'pe',
insulationThickness: 0.2,
twistPitch: 12,
pair2TwistRatio: 1.0,
cableLength: 100,
R: 0,
L: 0,
C: 0,
G: 0
}
})),
// 初始化数据
// 重置到默认状态
resetdeviceStore: () => {
set(() => ({
// 当前安装设备
installedDevices: { main: null, remote: null },
// 主机已安装模块
mainUnitModules: [],
// 远端已安装模块
@@ -458,6 +630,20 @@ const useDeviceStore = create(
connectionStatus: [],
// 报告数据
reports: [],
// 线缆参数
cableParams: {
conductorMaterial: 'copper',
coreDiameter: 0.57,
insulationMaterial: 'pe',
insulationThickness: 0.2,
twistPitch: 12,
pair2TwistRatio: 1.0,
cableLength: 100,
R: 0,
L: 0,
C: 0,
G: 0
}
}));
},
@@ -466,7 +652,8 @@ const useDeviceStore = create(
})
//devtools
// )
)
);

View File

@@ -52,3 +52,14 @@ img {
outline-color: rgba(0, 255, 255, 0.8);
}
}
/* 隐藏滚动条但保留滚动功能 */
.no-scrollbar {
-ms-overflow-style: none; /* IE and old Edge */
scrollbar-width: none; /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, new Edge */
width: 0;
height: 0;
}