@templmf/temp-solf-lmf 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1103 +0,0 @@
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
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/iview/3.5.4/styles/iview.css">
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
9
- <script src="https://cdnjs.cloudflare.com/ajax/libs/iview/3.5.4/iview.min.js"></script>
10
- <style>
11
- body {
12
- margin: 0;
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
14
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
15
- min-height: 100vh;
16
- }
17
-
18
- .dashboard-container {
19
- display: flex;
20
- height: 100vh;
21
- }
22
-
23
- .sidebar {
24
- width: 280px;
25
- background: rgba(255, 255, 255, 0.95);
26
- backdrop-filter: blur(10px);
27
- border-right: 1px solid rgba(102, 126, 234, 0.1);
28
- box-shadow: 0 8px 32px rgba(102, 126, 234, 0.1);
29
- }
30
-
31
- .sidebar-header {
32
- padding: 32px 24px 24px;
33
- border-bottom: 1px solid rgba(102, 126, 234, 0.1);
34
- background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
35
- color: white;
36
- }
37
-
38
- .sidebar-title {
39
- font-size: 20px;
40
- font-weight: 700;
41
- margin-bottom: 8px;
42
- }
43
-
44
- .sidebar-subtitle {
45
- font-size: 14px;
46
- opacity: 0.8;
47
- }
48
-
49
- .sidebar-nav {
50
- padding: 24px 16px;
51
- }
52
-
53
- .nav-item {
54
- width: 100%;
55
- margin-bottom: 8px;
56
- padding: 12px 16px;
57
- border: none;
58
- background: transparent;
59
- text-align: left;
60
- border-radius: 8px;
61
- transition: all 0.3s ease;
62
- font-size: 14px;
63
- color: #666;
64
- cursor: pointer;
65
- }
66
-
67
- .nav-item:hover {
68
- background: rgba(24, 144, 255, 0.08);
69
- color: #1890ff;
70
- transform: translateX(4px);
71
- }
72
-
73
- .nav-item.active {
74
- background: linear-gradient(135deg, #1890ff, #096dd9);
75
- color: white;
76
- box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
77
- }
78
-
79
- .legend-box {
80
- position: absolute;
81
- bottom: 24px;
82
- left: 16px;
83
- right: 16px;
84
- background: rgba(24, 144, 255, 0.05);
85
- border: 1px solid rgba(24, 144, 255, 0.1);
86
- border-radius: 12px;
87
- padding: 20px;
88
- }
89
-
90
- .legend-title {
91
- font-size: 14px;
92
- font-weight: 600;
93
- color: #1890ff;
94
- margin-bottom: 12px;
95
- }
96
-
97
- .legend-item {
98
- display: flex;
99
- align-items: center;
100
- margin-bottom: 8px;
101
- font-size: 12px;
102
- color: #666;
103
- }
104
-
105
- .legend-dot {
106
- width: 12px;
107
- height: 12px;
108
- border-radius: 50%;
109
- margin-right: 8px;
110
- }
111
-
112
- .main-content {
113
- flex: 1;
114
- overflow-y: auto;
115
- padding: 32px;
116
- background: rgba(255, 255, 255, 0.05);
117
- }
118
-
119
- .stats-grid {
120
- display: grid;
121
- grid-template-columns: repeat(4, 1fr);
122
- gap: 24px;
123
- margin-bottom: 32px;
124
- }
125
-
126
- .stat-card {
127
- background: rgba(255, 255, 255, 0.95);
128
- backdrop-filter: blur(10px);
129
- border-radius: 16px;
130
- padding: 24px;
131
- border: 1px solid rgba(24, 144, 255, 0.1);
132
- box-shadow: 0 8px 32px rgba(24, 144, 255, 0.08);
133
- transition: all 0.3s ease;
134
- }
135
-
136
- .stat-card:hover {
137
- transform: translateY(-4px);
138
- box-shadow: 0 16px 48px rgba(24, 144, 255, 0.15);
139
- }
140
-
141
- .stat-header {
142
- display: flex;
143
- justify-content: space-between;
144
- align-items: flex-start;
145
- margin-bottom: 16px;
146
- }
147
-
148
- .stat-label {
149
- font-size: 14px;
150
- color: #666;
151
- margin-bottom: 8px;
152
- }
153
-
154
- .stat-value {
155
- font-size: 32px;
156
- font-weight: 700;
157
- color: #1890ff;
158
- line-height: 1;
159
- }
160
-
161
- .stat-icon {
162
- width: 48px;
163
- height: 48px;
164
- border-radius: 12px;
165
- display: flex;
166
- align-items: center;
167
- justify-content: center;
168
- font-size: 20px;
169
- color: white;
170
- }
171
-
172
- .toolbar {
173
- background: rgba(255, 255, 255, 0.95);
174
- backdrop-filter: blur(10px);
175
- border-radius: 16px;
176
- padding: 20px 24px;
177
- margin-bottom: 24px;
178
- border: 1px solid rgba(24, 144, 255, 0.1);
179
- box-shadow: 0 4px 24px rgba(24, 144, 255, 0.08);
180
- }
181
-
182
- .toolbar-content {
183
- display: flex;
184
- justify-content: space-between;
185
- align-items: center;
186
- }
187
-
188
- .breadcrumb-nav {
189
- display: flex;
190
- align-items: center;
191
- gap: 8px;
192
- }
193
-
194
- .breadcrumb-item {
195
- background: none;
196
- border: none;
197
- color: #1890ff;
198
- cursor: pointer;
199
- font-size: 14px;
200
- padding: 4px 8px;
201
- border-radius: 4px;
202
- transition: all 0.2s ease;
203
- }
204
-
205
- .breadcrumb-item:hover {
206
- background: rgba(24, 144, 255, 0.08);
207
- }
208
-
209
- .breadcrumb-item.current {
210
- color: #333;
211
- cursor: default;
212
- }
213
-
214
- .toolbar-actions {
215
- display: flex;
216
- gap: 12px;
217
- align-items: center;
218
- }
219
-
220
- .content-area {
221
- background: rgba(255, 255, 255, 0.95);
222
- backdrop-filter: blur(10px);
223
- border-radius: 16px;
224
- padding: 32px;
225
- border: 1px solid rgba(24, 144, 255, 0.1);
226
- box-shadow: 0 8px 32px rgba(24, 144, 255, 0.08);
227
- }
228
-
229
- .domain-grid {
230
- display: grid;
231
- grid-template-columns: repeat(3, 1fr);
232
- gap: 32px;
233
- }
234
-
235
- .system-grid {
236
- display: grid;
237
- grid-template-columns: repeat(2, 1fr);
238
- gap: 32px;
239
- }
240
-
241
- .card {
242
- background: rgba(255, 255, 255, 0.9);
243
- border-radius: 16px;
244
- padding: 24px;
245
- cursor: pointer;
246
- transition: all 0.3s ease;
247
- border: 2px solid transparent;
248
- position: relative;
249
- overflow: hidden;
250
- }
251
-
252
- .card::before {
253
- content: '';
254
- position: absolute;
255
- top: 0;
256
- left: 0;
257
- right: 0;
258
- height: 4px;
259
- background: linear-gradient(90deg, #1890ff, #096dd9);
260
- transform: scaleX(0);
261
- transition: transform 0.3s ease;
262
- }
263
-
264
- .card:hover::before {
265
- transform: scaleX(1);
266
- }
267
-
268
- .card:hover {
269
- transform: translateY(-8px);
270
- box-shadow: 0 16px 48px rgba(24, 144, 255, 0.2);
271
- border-color: rgba(24, 144, 255, 0.2);
272
- }
273
-
274
- .card-header {
275
- display: flex;
276
- justify-content: space-between;
277
- align-items: flex-start;
278
- margin-bottom: 24px;
279
- }
280
-
281
- .card-title {
282
- font-size: 18px;
283
- font-weight: 700;
284
- color: #1890ff;
285
- margin: 0;
286
- }
287
-
288
- .trend-icon {
289
- width: 24px;
290
- height: 24px;
291
- border-radius: 50%;
292
- display: flex;
293
- align-items: center;
294
- justify-content: center;
295
- font-size: 14px;
296
- }
297
-
298
- .rate-section {
299
- margin-bottom: 24px;
300
- }
301
-
302
- .rate-label {
303
- font-size: 14px;
304
- color: #666;
305
- margin-bottom: 8px;
306
- }
307
-
308
- .rate-value {
309
- font-size: 36px;
310
- font-weight: 700;
311
- color: #1890ff;
312
- line-height: 1;
313
- margin-bottom: 12px;
314
- }
315
-
316
- .progress-bar {
317
- width: 100%;
318
- height: 8px;
319
- background: rgba(24, 144, 255, 0.1);
320
- border-radius: 4px;
321
- overflow: hidden;
322
- position: relative;
323
- }
324
-
325
- .progress-fill {
326
- height: 100%;
327
- background: linear-gradient(90deg, #1890ff, #096dd9);
328
- border-radius: 4px;
329
- transition: width 0.6s ease;
330
- position: relative;
331
- }
332
-
333
- .progress-fill::after {
334
- content: '';
335
- position: absolute;
336
- top: 0;
337
- left: 0;
338
- right: 0;
339
- bottom: 0;
340
- background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
341
- animation: shimmer 2s infinite;
342
- }
343
-
344
- @keyframes shimmer {
345
- 0% { transform: translateX(-100%); }
346
- 100% { transform: translateX(100%); }
347
- }
348
-
349
- .card-stats {
350
- display: grid;
351
- grid-template-columns: repeat(2, 1fr);
352
- gap: 16px;
353
- }
354
-
355
- .stat-box {
356
- background: rgba(24, 144, 255, 0.05);
357
- border: 1px solid rgba(24, 144, 255, 0.1);
358
- border-radius: 8px;
359
- padding: 16px;
360
- text-align: center;
361
- }
362
-
363
- .stat-number {
364
- font-size: 20px;
365
- font-weight: 700;
366
- color: #1890ff;
367
- margin-bottom: 4px;
368
- }
369
-
370
- .stat-text {
371
- font-size: 12px;
372
- color: #666;
373
- }
374
-
375
- .project-list {
376
- margin-top: 16px;
377
- padding-top: 16px;
378
- border-top: 1px solid rgba(24, 144, 255, 0.1);
379
- }
380
-
381
- .project-item {
382
- display: flex;
383
- justify-content: space-between;
384
- align-items: center;
385
- padding: 8px 0;
386
- font-size: 12px;
387
- }
388
-
389
- .project-name {
390
- color: #333;
391
- flex: 1;
392
- margin-right: 8px;
393
- overflow: hidden;
394
- text-overflow: ellipsis;
395
- white-space: nowrap;
396
- }
397
-
398
- .status-tag {
399
- padding: 4px 8px;
400
- border-radius: 12px;
401
- font-size: 10px;
402
- font-weight: 500;
403
- }
404
-
405
- .status-running { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
406
- .status-maintenance { background: #fffbe6; color: #faad14; border: 1px solid #ffe58f; }
407
- .status-error { background: #fff2f0; color: #ff4d4f; border: 1px solid #ffccc7; }
408
-
409
- .project-card {
410
- background: rgba(255, 255, 255, 0.9);
411
- border-radius: 16px;
412
- padding: 32px;
413
- margin-bottom: 24px;
414
- border: 2px solid rgba(24, 144, 255, 0.1);
415
- position: relative;
416
- overflow: hidden;
417
- }
418
-
419
- .project-header {
420
- display: flex;
421
- justify-content: space-between;
422
- align-items: flex-start;
423
- margin-bottom: 32px;
424
- }
425
-
426
- .project-title {
427
- font-size: 24px;
428
- font-weight: 700;
429
- color: #1890ff;
430
- margin-bottom: 12px;
431
- }
432
-
433
- .project-rate {
434
- text-align: right;
435
- }
436
-
437
- .project-rate-value {
438
- font-size: 32px;
439
- font-weight: 700;
440
- color: #1890ff;
441
- margin-bottom: 4px;
442
- }
443
-
444
- .project-rate-label {
445
- font-size: 14px;
446
- color: #666;
447
- }
448
-
449
- .indicators-grid {
450
- display: grid;
451
- grid-template-columns: repeat(3, 1fr);
452
- gap: 24px;
453
- }
454
-
455
- .indicator-card {
456
- background: rgba(24, 144, 255, 0.03);
457
- border: 1px solid rgba(24, 144, 255, 0.1);
458
- border-radius: 12px;
459
- padding: 24px;
460
- text-align: center;
461
- position: relative;
462
- overflow: hidden;
463
- }
464
-
465
- .indicator-card::before {
466
- content: '';
467
- position: absolute;
468
- top: 0;
469
- left: 0;
470
- right: 0;
471
- height: 3px;
472
- background: linear-gradient(90deg, #1890ff, #096dd9);
473
- }
474
-
475
- .indicator-label {
476
- font-size: 14px;
477
- font-weight: 600;
478
- color: #666;
479
- margin-bottom: 16px;
480
- }
481
-
482
- .indicator-value {
483
- font-size: 48px;
484
- font-weight: 700;
485
- color: #1890ff;
486
- margin-bottom: 12px;
487
- line-height: 1;
488
- }
489
-
490
- .indicator-status {
491
- padding: 6px 12px;
492
- border-radius: 16px;
493
- font-size: 12px;
494
- font-weight: 600;
495
- margin-bottom: 16px;
496
- }
497
-
498
- .status-pass {
499
- background: #f6ffed;
500
- color: #52c41a;
501
- border: 1px solid #b7eb8f;
502
- }
503
-
504
- .status-fail {
505
- background: #fff2f0;
506
- color: #ff4d4f;
507
- border: 1px solid #ffccc7;
508
- }
509
-
510
- .indicator-progress {
511
- width: 100%;
512
- height: 6px;
513
- background: rgba(24, 144, 255, 0.1);
514
- border-radius: 3px;
515
- overflow: hidden;
516
- }
517
-
518
- .indicator-progress-fill {
519
- height: 100%;
520
- border-radius: 3px;
521
- transition: width 0.8s ease;
522
- }
523
-
524
- .progress-excellent { background: linear-gradient(90deg, #52c41a, #73d13d); }
525
- .progress-good { background: linear-gradient(90deg, #1890ff, #40a9ff); }
526
- .progress-poor { background: linear-gradient(90deg, #ff4d4f, #ff7875); }
527
-
528
- .back-btn {
529
- background: rgba(24, 144, 255, 0.1);
530
- border: 1px solid rgba(24, 144, 255, 0.2);
531
- color: #1890ff;
532
- border-radius: 8px;
533
- padding: 8px 16px;
534
- font-size: 14px;
535
- cursor: pointer;
536
- transition: all 0.2s ease;
537
- }
538
-
539
- .back-btn:hover {
540
- background: rgba(24, 144, 255, 0.2);
541
- transform: translateX(-2px);
542
- }
543
-
544
- .search-input {
545
- width: 280px;
546
- }
547
-
548
- .ivu-input-wrapper {
549
- border-radius: 8px;
550
- }
551
-
552
- .ivu-select {
553
- width: 140px;
554
- }
555
-
556
- .ivu-btn {
557
- border-radius: 8px;
558
- font-weight: 500;
559
- }
560
-
561
- .chevron-icon {
562
- color: #ccc;
563
- margin: 0 4px;
564
- }
565
-
566
- /* 自定义滚动条 */
567
- ::-webkit-scrollbar {
568
- width: 6px;
569
- }
570
-
571
- ::-webkit-scrollbar-track {
572
- background: rgba(24, 144, 255, 0.1);
573
- border-radius: 3px;
574
- }
575
-
576
- ::-webkit-scrollbar-thumb {
577
- background: rgba(24, 144, 255, 0.3);
578
- border-radius: 3px;
579
- }
580
-
581
- ::-webkit-scrollbar-thumb:hover {
582
- background: rgba(24, 144, 255, 0.5);
583
- }
584
- </style>
585
- </head>
586
- <body>
587
- <div id="app">
588
- <div class="dashboard-container">
589
- <!-- 侧边栏 -->
590
- <div class="sidebar">
591
- <div class="sidebar-header">
592
- <div class="sidebar-title">达标率看板</div>
593
- <div class="sidebar-subtitle">多层级监控平台</div>
594
- </div>
595
-
596
- <div class="sidebar-nav">
597
- <button
598
- class="nav-item"
599
- :class="{ active: currentView === 'domain' }"
600
- @click="navigateToDomain"
601
- >
602
- <Icon type="md-grid" style="margin-right: 8px;" />
603
- 领域总览
604
- </button>
605
-
606
- <button
607
- v-if="selectedDomain"
608
- class="nav-item"
609
- :class="{ active: currentView === 'system' }"
610
- @click="navigateToSystem"
611
- >
612
- <Icon type="md-browsers" style="margin-right: 8px;" />
613
- {{ selectedDomain }}
614
- </button>
615
-
616
- <button
617
- v-if="selectedSystem"
618
- class="nav-item"
619
- :class="{ active: currentView === 'project' }"
620
- @click="navigateToProject"
621
- >
622
- <Icon type="md-cube" style="margin-right: 8px;" />
623
- {{ selectedSystem }}
624
- </button>
625
- </div>
626
-
627
- <div class="legend-box">
628
- <div class="legend-title">达标率说明</div>
629
- <div class="legend-item">
630
- <div class="legend-dot" style="background: #52c41a;"></div>
631
- <span>≥85% 优秀</span>
632
- </div>
633
- <div class="legend-item">
634
- <div class="legend-dot" style="background: #1890ff;"></div>
635
- <span>70-84% 良好</span>
636
- </div>
637
- <div class="legend-item">
638
- <div class="legend-dot" style="background: #ff4d4f;"></div>
639
- <span>&lt;70% 需改进</span>
640
- </div>
641
- </div>
642
- </div>
643
-
644
- <!-- 主内容区 -->
645
- <div class="main-content">
646
- <!-- 顶部统计卡片 -->
647
- <div class="stats-grid">
648
- <div class="stat-card">
649
- <div class="stat-header">
650
- <div>
651
- <div class="stat-label">总体达标率</div>
652
- <div class="stat-value">{{ overallStats.avgRate }}%</div>
653
- </div>
654
- <div class="stat-icon" style="background: linear-gradient(135deg, #1890ff, #096dd9);">
655
- <Icon type="md-analytics" />
656
- </div>
657
- </div>
658
- </div>
659
-
660
- <div class="stat-card">
661
- <div class="stat-header">
662
- <div>
663
- <div class="stat-label">领域数量</div>
664
- <div class="stat-value">{{ overallStats.domains }}</div>
665
- </div>
666
- <div class="stat-icon" style="background: linear-gradient(135deg, #52c41a, #389e0d);">
667
- <Icon type="md-apps" />
668
- </div>
669
- </div>
670
- </div>
671
-
672
- <div class="stat-card">
673
- <div class="stat-header">
674
- <div>
675
- <div class="stat-label">系统数量</div>
676
- <div class="stat-value">{{ overallStats.systems }}</div>
677
- </div>
678
- <div class="stat-icon" style="background: linear-gradient(135deg, #722ed1, #531dab);">
679
- <Icon type="md-settings" />
680
- </div>
681
- </div>
682
- </div>
683
-
684
- <div class="stat-card">
685
- <div class="stat-header">
686
- <div>
687
- <div class="stat-label">工程总数</div>
688
- <div class="stat-value">{{ overallStats.projects }}</div>
689
- </div>
690
- <div class="stat-icon" style="background: linear-gradient(135deg, #fa8c16, #d46b08);">
691
- <Icon type="md-construct" />
692
- </div>
693
- </div>
694
- </div>
695
- </div>
696
-
697
- <!-- 工具栏 -->
698
- <div class="toolbar">
699
- <div class="toolbar-content">
700
- <div class="breadcrumb-nav">
701
- <template v-for="(item, index) in breadcrumb">
702
- <button
703
- :key="'btn-' + index"
704
- class="breadcrumb-item"
705
- :class="{ current: index === breadcrumb.length - 1 }"
706
- @click="handleBreadcrumbClick(index)"
707
- >
708
- {{ item }}
709
- </button>
710
- <span v-if="index < breadcrumb.length - 1" :key="'sep-' + index" class="chevron-icon">
711
- <Icon type="ios-arrow-forward" />
712
- </span>
713
- </template>
714
- </div>
715
-
716
- <div class="toolbar-actions">
717
- <Button
718
- v-if="currentView !== 'domain'"
719
- class="back-btn"
720
- @click="handleBack"
721
- icon="md-arrow-back"
722
- size="small"
723
- >
724
- 返回
725
- </Button>
726
-
727
- <Input
728
- v-model="searchTerm"
729
- placeholder="搜索..."
730
- prefix="ios-search"
731
- class="search-input"
732
- size="small"
733
- />
734
-
735
- <Select v-model="filterStatus" size="small" style="width: 120px;">
736
- <Option value="all">所有状态</Option>
737
- <Option value="运行中">运行中</Option>
738
- <Option value="维护中">维护中</Option>
739
- <Option value="异常">异常</Option>
740
- </Select>
741
-
742
- <Button icon="md-refresh" size="small">刷新</Button>
743
- <Button icon="md-download" size="small">导出</Button>
744
- </div>
745
- </div>
746
- </div>
747
-
748
- <!-- 主要内容区域 -->
749
- <div class="content-area">
750
- <!-- 领域视图 -->
751
- <div v-if="currentView === 'domain'" class="domain-grid">
752
- <div
753
- v-for="domain in domainStats"
754
- :key="domain.name"
755
- class="card"
756
- @click="handleDomainClick(domain.name)"
757
- >
758
- <div class="card-header">
759
- <h3 class="card-title">{{ domain.name }}</h3>
760
- <div class="trend-icon" :style="getTrendIconStyle(domain.rate)">
761
- <Icon :type="getTrendIcon(domain.rate)" />
762
- </div>
763
- </div>
764
-
765
- <div class="rate-section">
766
- <div class="rate-label">达标率</div>
767
- <div class="rate-value">{{ domain.rate.toFixed(1) }}%</div>
768
- <div class="progress-bar">
769
- <div
770
- class="progress-fill"
771
- :style="{ width: domain.rate + '%' }"
772
- ></div>
773
- </div>
774
- </div>
775
-
776
- <div class="card-stats">
777
- <div class="stat-box">
778
- <div class="stat-number">{{ domain.systemCount }}</div>
779
- <div class="stat-text">系统</div>
780
- </div>
781
- <div class="stat-box">
782
- <div class="stat-number">{{ domain.projectCount }}</div>
783
- <div class="stat-text">工程</div>
784
- </div>
785
- </div>
786
- </div>
787
- </div>
788
-
789
- <!-- 系统视图 -->
790
- <div v-if="currentView === 'system'" class="system-grid">
791
- <div
792
- v-for="system in systemStats"
793
- :key="system.name"
794
- class="card"
795
- @click="handleSystemClick(system.name)"
796
- >
797
- <div class="card-header">
798
- <h3 class="card-title">{{ system.name }}</h3>
799
- <div class="trend-icon" :style="getTrendIconStyle(system.rate)">
800
- <Icon :type="getTrendIcon(system.rate)" />
801
- </div>
802
- </div>
803
-
804
- <div class="rate-section">
805
- <div class="rate-label">达标率</div>
806
- <div class="rate-value">{{ system.rate.toFixed(1) }}%</div>
807
- <div class="progress-bar">
808
- <div
809
- class="progress-fill"
810
- :style="{ width: system.rate + '%' }"
811
- ></div>
812
- </div>
813
- </div>
814
-
815
- <div class="stat-box">
816
- <div class="stat-number">{{ system.projectCount }}</div>
817
- <div class="stat-text">个工程</div>
818
- </div>
819
-
820
- <div class="project-list">
821
- <div
822
- v-for="(project, index) in system.projects.slice(0, 3)"
823
- :key="index"
824
- class="project-item"
825
- >
826
- <span class="project-name">{{ project[0] }}</span>
827
- <span class="status-tag" :class="getStatusClass(project[1].status)">
828
- {{ project[1].status }}
829
- </span>
830
- </div>
831
- <div v-if="system.projects.length > 3" class="project-item" style="justify-content: center; color: #999;">
832
- 还有 {{ system.projects.length - 3 }} 个工程...
833
- </div>
834
- </div>
835
- </div>
836
- </div>
837
-
838
- <!-- 工程视图 -->
839
- <div v-if="currentView === 'project'">
840
- <div
841
- v-for="project in projectStats"
842
- :key="project.name"
843
- class="project-card"
844
- >
845
- <div class="project-header">
846
- <div>
847
- <h3 class="project-title">{{ project.name }}</h3>
848
- <Tag :color="getStatusTagColor(project.status)">{{ project.status }}</Tag>
849
- </div>
850
- <div class="project-rate">
851
- <div class="project-rate-value">{{ project.rate.toFixed(1) }}%</div>
852
- <div class="project-rate-label">达标率</div>
853
- </div>
854
- </div>
855
-
856
- <div class="indicators-grid">
857
- <div
858
- v-for="(score, index) in project.indicators"
859
- :key="index"
860
- class="indicator-card"
861
- >
862
- <div class="indicator-label">指标 {{ index + 1 }}</div>
863
- <div class="indicator-value">{{ score }}</div>
864
- <div class="indicator-status" :class="score >= 80 ? 'status-pass' : 'status-fail'">
865
- {{ score >= 80 ? '达标' : '未达标' }}
866
- </div>
867
- <div class="indicator-progress">
868
- <div
869
- class="indicator-progress-fill"
870
- :class="getProgressClass(score)"
871
- :style="{ width: score + '%' }"
872
- ></div>
873
- </div>
874
- </div>
875
- </div>
876
- </div>
877
- </div>
878
- </div>
879
- </div>
880
- </div>
881
- </div>
882
-
883
- <script>
884
- new Vue({
885
- el: '#app',
886
- data() {
887
- return {
888
- // 模拟数据
889
- mockData: {
890
- "智能制造": {
891
- "生产管理系统": {
892
- "MES工程": { indicators: [85, 92, 78], status: "运行中" },
893
- "WMS工程": { indicators: [90, 88, 95], status: "运行中" },
894
- "质量管理工程": { indicators: [76, 82, 89], status: "维护中" }
895
- },
896
- "设备监控系统": {
897
- "设备状态监控工程": { indicators: [92, 87, 91], status: "运行中" },
898
- "预测维护工程": { indicators: [68, 74, 82], status: "异常" },
899
- "能耗管理工程": { indicators: [89, 93, 85], status: "运行中" }
900
- }
901
- },
902
- "数字化办公": {
903
- "协同办公系统": {
904
- "OA系统工程": { indicators: [88, 91, 87], status: "运行中" },
905
- "文档管理工程": { indicators: [92, 89, 94], status: "运行中" },
906
- "视频会议工程": { indicators: [79, 85, 88], status: "运行中" }
907
- },
908
- "人事管理系统": {
909
- "招聘管理工程": { indicators: [85, 78, 90], status: "运行中" },
910
- "绩效考核工程": { indicators: [72, 88, 85], status: "运行中" },
911
- "薪资管理工程": { indicators: [95, 92, 89], status: "运行中" }
912
- }
913
- },
914
- "数据分析": {
915
- "商业智能系统": {
916
- "报表平台工程": { indicators: [91, 86, 88], status: "运行中" },
917
- "数据挖掘工程": { indicators: [77, 82, 79], status: "运行中" },
918
- "实时分析工程": { indicators: [83, 89, 91], status: "运行中" }
919
- }
920
- }
921
- },
922
- currentView: 'domain',
923
- selectedDomain: null,
924
- selectedSystem: null,
925
- breadcrumb: ['领域总览'],
926
- searchTerm: '',
927
- filterStatus: 'all'
928
- };
929
- },
930
- computed: {
931
- // 计算整体统计
932
- overallStats() {
933
- const domains = Object.keys(this.mockData).length;
934
- const systems = Object.values(this.mockData).reduce((sum, domain) => sum + Object.keys(domain).length, 0);
935
- const projects = Object.values(this.mockData).reduce((sum, domain) =>
936
- sum + Object.values(domain).reduce((sSum, system) => sSum + Object.keys(system).length, 0), 0
937
- );
938
-
939
- const allRates = Object.values(this.mockData).map(domain => this.calculateDomainCompliance(domain));
940
- const avgRate = allRates.reduce((sum, rate) => sum + rate, 0) / allRates.length;
941
-
942
- return {
943
- domains,
944
- systems,
945
- projects,
946
- avgRate: avgRate.toFixed(1)
947
- };
948
- },
949
-
950
- // 领域统计
951
- domainStats() {
952
- return Object.entries(this.mockData).map(([domainName, domainData]) => ({
953
- name: domainName,
954
- rate: this.calculateDomainCompliance(domainData),
955
- systemCount: Object.keys(domainData).length,
956
- projectCount: Object.values(domainData).reduce((sum, system) => sum + Object.keys(system).length, 0)
957
- }));
958
- },
959
-
960
- // 系统统计
961
- systemStats() {
962
- if (!this.selectedDomain) return [];
963
- const systemData = this.mockData[this.selectedDomain];
964
- return Object.entries(systemData).map(([systemName, projects]) => ({
965
- name: systemName,
966
- rate: this.calculateSystemCompliance(projects),
967
- projectCount: Object.keys(projects).length,
968
- projects: Object.entries(projects)
969
- }));
970
- },
971
-
972
- // 工程统计
973
- projectStats() {
974
- if (!this.selectedDomain || !this.selectedSystem) return [];
975
- const projectData = this.mockData[this.selectedDomain][this.selectedSystem];
976
- return Object.entries(projectData).map(([projectName, project]) => ({
977
- name: projectName,
978
- indicators: project.indicators,
979
- status: project.status,
980
- rate: this.calculateComplianceRate(project.indicators)
981
- }));
982
- }
983
- },
984
- methods: {
985
- // 计算达标率 (≥80为达标)
986
- calculateComplianceRate(indicators) {
987
- const passCount = indicators.filter(score => score >= 80).length;
988
- return (passCount / indicators.length) * 100;
989
- },
990
-
991
- // 计算系统达标率
992
- calculateSystemCompliance(systemData) {
993
- const projects = Object.values(systemData);
994
- const totalRate = projects.reduce((sum, project) =>
995
- sum + this.calculateComplianceRate(project.indicators), 0
996
- );
997
- return totalRate / projects.length;
998
- },
999
-
1000
- // 计算领域达标率
1001
- calculateDomainCompliance(domainData) {
1002
- const systems = Object.values(domainData);
1003
- const totalRate = systems.reduce((sum, system) =>
1004
- sum + this.calculateSystemCompliance(system), 0
1005
- );
1006
- return totalRate / systems.length;
1007
- },
1008
-
1009
- // 获取趋势图标
1010
- getTrendIcon(rate) {
1011
- if (rate >= 85) return 'md-trending-up';
1012
- if (rate >= 70) return 'md-remove';
1013
- return 'md-trending-down';
1014
- },
1015
-
1016
- // 获取趋势图标样式
1017
- getTrendIconStyle(rate) {
1018
- if (rate >= 85) return 'background: #52c41a; color: white;';
1019
- if (rate >= 70) return 'background: #1890ff; color: white;';
1020
- return 'background: #ff4d4f; color: white;';
1021
- },
1022
-
1023
- // 获取状态类名
1024
- getStatusClass(status) {
1025
- switch (status) {
1026
- case '运行中': return 'status-running';
1027
- case '维护中': return 'status-maintenance';
1028
- case '异常': return 'status-error';
1029
- default: return '';
1030
- }
1031
- },
1032
-
1033
- // 获取状态标签颜色
1034
- getStatusTagColor(status) {
1035
- switch (status) {
1036
- case '运行中': return 'success';
1037
- case '维护中': return 'warning';
1038
- case '异常': return 'error';
1039
- default: return 'default';
1040
- }
1041
- },
1042
-
1043
- // 获取进度条类名
1044
- getProgressClass(score) {
1045
- if (score >= 85) return 'progress-excellent';
1046
- if (score >= 70) return 'progress-good';
1047
- return 'progress-poor';
1048
- },
1049
-
1050
- // 导航处理
1051
- handleDomainClick(domainName) {
1052
- this.selectedDomain = domainName;
1053
- this.currentView = 'system';
1054
- this.breadcrumb = ['领域总览', domainName];
1055
- },
1056
-
1057
- handleSystemClick(systemName) {
1058
- this.selectedSystem = systemName;
1059
- this.currentView = 'project';
1060
- this.breadcrumb = ['领域总览', this.selectedDomain, systemName];
1061
- },
1062
-
1063
- handleBack() {
1064
- if (this.currentView === 'project') {
1065
- this.currentView = 'system';
1066
- this.breadcrumb = ['领域总览', this.selectedDomain];
1067
- } else if (this.currentView === 'system') {
1068
- this.currentView = 'domain';
1069
- this.selectedDomain = null;
1070
- this.breadcrumb = ['领域总览'];
1071
- }
1072
- },
1073
-
1074
- handleBreadcrumbClick(index) {
1075
- if (index === 0) {
1076
- this.navigateToDomain();
1077
- } else if (index === 1 && this.selectedDomain) {
1078
- this.navigateToSystem();
1079
- }
1080
- },
1081
-
1082
- navigateToDomain() {
1083
- this.currentView = 'domain';
1084
- this.selectedDomain = null;
1085
- this.selectedSystem = null;
1086
- this.breadcrumb = ['领域总览'];
1087
- },
1088
-
1089
- navigateToSystem() {
1090
- this.currentView = 'system';
1091
- this.selectedSystem = null;
1092
- this.breadcrumb = ['领域总览', this.selectedDomain];
1093
- },
1094
-
1095
- navigateToProject() {
1096
- this.currentView = 'project';
1097
- this.breadcrumb = ['领域总览', this.selectedDomain, this.selectedSystem];
1098
- }
1099
- }
1100
- });
1101
- </script>
1102
- </body>
1103
- </html>