7
7
<div class =" text-secondary" >
8
8
<p >{{ t('ipInfos.Notes') }}</p >
9
9
</div >
10
- <div class =" row" >
11
- <div v-for =" (card, index) in ipDataCards.slice(0, ipCardsToShow)" :key =" card.id" :ref =" card.id" :class =" [colClass, {
10
+ <div class =" row" >
11
+ <div v-for =" (card, index) in ipDataCards.slice(0, ipCardsToShow)" :key =" card.id" :ref =" card.id" :class =" [colClass, {
12
12
'jn-opacity': !card.ip || card.ip === t('ipInfos.IPv4Error') || card.ip === t('ipInfos.IPv6Error')
13
13
}]" >
14
- <div class =" card jn-card keyboard-shortcut-card" :class =" {
14
+ <div class =" card jn-card keyboard-shortcut-card" :class =" {
15
15
'dark-mode dark-mode-border': isDarkMode,
16
16
'jn-ip-card1 jn-hover-card': !isMobile && ipGeoSource === 0,
17
17
'jn-ip-card2 jn-hover-card': !isMobile && ipGeoSource !== 0,
18
18
}" >
19
- <div class =" card-header jn-ip-title jn-link1"
20
- :class =" { 'dark-mode-title': isDarkMode, 'bg-light': !isDarkMode }" style =" font-weight : bold ;" >
21
- <span >
22
- <i class =" bi" :class =" 'bi-' + (index + 1) + '-circle-fill'" ></i >  ;
23
- {{ t('ipInfos.Source') }}: {{ card.source }}</span >
24
- <button @click =" refreshCard(card,index)"
25
- :class =" ['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"
26
- :aria-label =" 'Refresh' + card.source" v-tooltip =" t('Tooltips.RefreshIPCard')" >
27
- <i class =" bi bi-arrow-clockwise" ></i ></button >
28
- </div >
29
- <div class =" p-3 placeholder-glow " :class =" {
19
+ <div class =" card-header jn-ip-title jn-link1"
20
+ :class =" { 'dark-mode-title': isDarkMode, 'bg-light': !isDarkMode }" style =" font-weight : bold ;" >
21
+ <span >
22
+ <i class =" bi" :class =" 'bi-' + (index + 1) + '-circle-fill'" ></i >  ;
23
+ {{ t('ipInfos.Source') }}: {{ card.source }}</span >
24
+ <button @click =" refreshCard(card,index)"
25
+ :class =" ['btn', isDarkMode ? 'btn-dark dark-mode-refresh' : 'btn-light']"
26
+ :aria-label =" 'Refresh' + card.source" v-tooltip =" t('Tooltips.RefreshIPCard')" >
27
+ <i class =" bi bi-arrow-clockwise" ></i ></button >
28
+ </div >
29
+ <div class =" p-3 placeholder-glow " :class =" {
30
30
'dark-mode-title': isDarkMode,
31
31
'jn-link2-dark': isDarkMode,
32
32
'bg-light': !isDarkMode,
33
33
'jn-link2': !isDarkMode
34
34
}" >
35
- <span class =" jn-text col-auto" >
36
- <i class =" bi bi-pc-display-horizontal" ></i >  ;
37
- </span >
38
- <span v-if =" card.ip" class =" col-10" :class =" { 'jn-ip-font': (isMobile && card.ip.length > 32) }" >
39
- {{ card.ip }}  ;
40
- <i v-if =" isValidIP(card.ip)"
41
- :class =" copiedStatus[card.id] ? 'bi bi-clipboard-check-fill' : 'bi bi-clipboard-plus'"
42
- @click =" copyToClipboard(card.ip, card.id)" role =" button"
43
- v-tooltip =" { title: t('Tooltips.CopyIP'), placement: 'right' }" :aria-label =" 'Copy' + card.ip" ></i >
44
- </span >
45
- <span v-else class =" placeholder col-10" ></span >
46
- </div >
35
+ <span class =" jn-text col-auto" >
36
+ <i class =" bi bi-pc-display-horizontal" ></i >  ;
37
+ </span >
38
+ <span v-if =" card.ip" class =" col-10" :class =" { 'jn-ip-font': (isMobile && card.ip.length > 32) }" >
39
+ {{ card.ip }}  ;
40
+ <i v-if =" isValidIP(card.ip)"
41
+ :class =" copiedStatus[card.id] ? 'bi bi-clipboard-check-fill' : 'bi bi-clipboard-plus'"
42
+ @click =" copyToClipboard(card.ip, card.id)" role =" button"
43
+ v-tooltip =" { title: t('Tooltips.CopyIP'), placement: 'right' }" :aria-label =" 'Copy' + card.ip" ></i >
44
+ </span >
45
+ <span v-else class =" placeholder col-10" ></span >
46
+ </div >
47
47
48
48
49
- <div v-if =" (card.asn) || (card.ip === t('ipInfos.IPv4Error')) || (card.ip === t('ipInfos.IPv6Error') ) || card.ip === '2001:4860:4860::8888'
49
+ <div v-if =" (card.asn) || card.ip === '2001:4860:4860::8888'
50
50
" class =" card-body" :id =" 'IPInfo-' + (index + 1)" >
51
- <ul class =" list-group list-group-flush" v-if =" card.country_name" >
52
-
53
- <img v-if =" isMapShown" :src =" isDarkMode ? card.mapUrl_dark : card.mapUrl"
54
- class =" card-img-top jn-map-image" alt =" Map" >
55
-
56
- <li class =" jn-list-group-item"
57
- :class =" { 'dark-mode': isDarkMode, 'mobile-list': isMobile && isCardsCollapsed }" >
58
- <span class =" jn-text col-auto" >
59
- <i class =" bi bi-geo-alt-fill" ></i > {{ t('ipInfos.Country') }} :  ;
60
- </span >
61
- <span class =" col-10 " >
62
- {{ card.country_name }}
63
- <span v-if =" card.country_code" :class =" 'jn-fl fi fi-' + card.country_code.toLowerCase()" ></span >
51
+ <ul class =" list-group list-group-flush" v-if =" card.country_name" >
52
+
53
+ <img v-if =" isMapShown" :src =" isDarkMode ? card.mapUrl_dark : card.mapUrl"
54
+ class =" card-img-top jn-map-image" alt =" Map" >
55
+
56
+ <li class =" jn-list-group-item"
57
+ :class =" { 'dark-mode': isDarkMode, 'mobile-list': isMobile && isCardsCollapsed }" >
58
+ <span class =" jn-text col-auto" >
59
+ <i class =" bi bi-geo-alt-fill" ></i > {{ t('ipInfos.Country') }} :  ;
60
+ </span >
61
+ <span class =" col-10 " >
62
+ {{ card.country_name }}
63
+ <span v-if =" card.country_code" :class =" 'jn-fl fi fi-' + card.country_code.toLowerCase()" ></span >
64
+ </span >
65
+ </li >
66
+
67
+ <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
68
+ :class =" { 'dark-mode': isDarkMode }" >
69
+ <span class =" jn-text col-auto" >
70
+ <i class =" bi bi-houses" ></i >
71
+ {{ t('ipInfos.Region') }} :  ;
72
+ </span >
73
+ <span class =" col-10 " >
74
+ {{ card.region }}
75
+ </span >
76
+ </li >
77
+
78
+ <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
79
+ :class =" { 'dark-mode': isDarkMode }" >
80
+ <span class =" jn-text col-auto" >
81
+ <i class =" bi bi-sign-turn-right" ></i >
82
+ {{ t('ipInfos.City') }} :  ;
83
+ </span >
84
+ <span class =" col-10 " >
85
+ {{ card.city }}
86
+ </span >
87
+ </li >
88
+
89
+ <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
90
+ :class =" { 'dark-mode': isDarkMode }" >
91
+ <span class =" jn-text col-auto" >
92
+ <i class =" bi bi-ethernet" ></i >
93
+ {{ t('ipInfos.ISP') }} :  ;
94
+ </span >
95
+ <span class =" col-10 " >
96
+ {{ card.isp }}
97
+ </span >
98
+ </li >
99
+
100
+ <li
101
+ v-show =" (!isMobile || !isCardsCollapsed) && ipGeoSource === 0 && card.type !== t('ipInfos.proxyDetect.type.unknownType')"
102
+ class =" jn-list-group-item" :class =" { 'dark-mode': isDarkMode }" >
103
+ <span class =" jn-text col-auto" >
104
+ <i class =" bi bi-reception-4" ></i >
105
+ {{ t('ipInfos.type') }} :  ;
106
+ </span >
107
+ <span class =" col-10 " >
108
+ {{ card.type }}
109
+ <span v-if =" card.proxyOperator !== 'unknown'" >
110
+ ( {{ card.proxyOperator }} )
64
111
</span >
65
- </li >
66
-
67
- <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
68
- :class =" { 'dark-mode': isDarkMode }" >
69
- <span class =" jn-text col-auto" >
70
- <i class =" bi bi-houses" ></i >
71
- {{ t('ipInfos.Region') }} :  ;
112
+ </span >
113
+ </li >
114
+
115
+ <li
116
+ v-show =" (!isMobile || !isCardsCollapsed) && ipGeoSource === 0 && card.isProxy !== t('ipInfos.proxyDetect.unknownProxyType')"
117
+ class =" jn-list-group-item" :class =" { 'dark-mode': isDarkMode }" >
118
+ <span class =" jn-text col-auto" >
119
+ <i class =" bi bi-shield-fill-check" ></i >
120
+ {{ t('ipInfos.isProxy') }} :  ;
121
+ </span >
122
+ <span class =" col-10 " >
123
+ {{ card.isProxy }}
124
+ <span v-if =" card.proxyProtocol !== t('ipInfos.proxyDetect.unknownProtocol')" >
125
+ ( {{ card.proxyProtocol }} )
72
126
</span >
73
- <span class =" col-10 " >
74
- {{ card.region }}
75
- </span >
76
- </li >
77
-
78
- <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
79
- :class =" { 'dark-mode': isDarkMode }" >
80
- <span class =" jn-text col-auto" >
81
- <i class =" bi bi-sign-turn-right" ></i >
82
- {{ t('ipInfos.City') }} :  ;
83
- </span >
84
- <span class =" col-10 " >
85
- {{ card.city }}
127
+ </span >
128
+ </li >
129
+
130
+ <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item border-0"
131
+ :class =" { 'dark-mode': isDarkMode }" >
132
+ <span class =" jn-text col-auto" >
133
+ <i class =" bi bi-buildings" ></i >
134
+ {{ t('ipInfos.ASN') }} :  ;
135
+ </span >
136
+ <span v-if =" card.asnlink" class =" col-9 " >
137
+ {{ card.asn }} <i v-if =" configs.cloudFlare" class =" bi bi-info-circle"
138
+ @click =" getASNInfo(card.asn, index)" data-bs-toggle =" collapse"
139
+ :data-bs-target =" '#' + 'collapseASNInfo-' + index" aria-expanded =" false"
140
+ :aria-controls =" 'collapseASNInfo-' + index" role =" button"
141
+ :aria-label =" 'Display AS Info of' + card.asn"
142
+ v-tooltip =" { title: t('Tooltips.ShowASNInfo'), placement: 'right' }" ></i >
143
+ </span >
144
+ </li >
145
+
146
+ <div class =" collapse alert alert-light placeholder-glow lh-lg fw-bold p-0"
147
+ :id =" 'collapseASNInfo-' + index" :data-bs-theme =" isDarkMode ? 'dark' : ''" >
148
+
149
+ <!-- 通过将 collapse 的 padding 设置为 0,然后添加一个子 div 设置 padding 的方式,避免 Bootstrap 的 collapse 发生卡顿,很奇怪的 bug -->
150
+
151
+ <div class =" p-3" >
152
+ <span v-if =" asnInfos[card.asn]" >
153
+ <i class =" bi bi-info-circle-fill" ></i > <span class =" fw-light" >{{ t('ipInfos.ASNInfo.note')
154
+ }}</span >
155
+ <br />
156
+ <template v-for =" (item ,key ) in asnInfos [card .asn ] " >
157
+ <span class =" fw-light" >
158
+ {{ t(`ipInfos.ASNInfo.${key}`) }}
159
+ </span >
160
+ {{ item }}
161
+ <br />
162
+ </template >
86
163
</span >
87
- </li >
88
164
89
- <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item"
90
- :class =" { 'dark-mode': isDarkMode }" >
91
- <span class =" jn-text col-auto" >
92
- <i class =" bi bi-ethernet" ></i >
93
- {{ t('ipInfos.ISP') }} :  ;
94
- </span >
95
- <span class =" col-10 " >
96
- {{ card.isp }}
97
- </span >
98
- </li >
99
-
100
- <li
101
- v-show =" (!isMobile || !isCardsCollapsed) && ipGeoSource === 0 && card.type !== t('ipInfos.proxyDetect.type.unknownType')"
102
- class =" jn-list-group-item" :class =" { 'dark-mode': isDarkMode }" >
103
- <span class =" jn-text col-auto" >
104
- <i class =" bi bi-reception-4" ></i >
105
- {{ t('ipInfos.type') }} :  ;
106
- </span >
107
- <span class =" col-10 " >
108
- {{ card.type }}
109
- <span v-if =" card.proxyOperator !== 'unknown'" >
110
- ( {{ card.proxyOperator }} )
111
- </span >
112
- </span >
113
- </li >
114
-
115
- <li
116
- v-show =" (!isMobile || !isCardsCollapsed) && ipGeoSource === 0 && card.isProxy !== t('ipInfos.proxyDetect.unknownProxyType')"
117
- class =" jn-list-group-item" :class =" { 'dark-mode': isDarkMode }" >
118
- <span class =" jn-text col-auto" >
119
- <i class =" bi bi-shield-fill-check" ></i >
120
- {{ t('ipInfos.isProxy') }} :  ;
121
- </span >
122
- <span class =" col-10 " >
123
- {{ card.isProxy }}
124
- <span v-if =" card.proxyProtocol !== t('ipInfos.proxyDetect.unknownProtocol')" >
125
- ( {{ card.proxyProtocol }} )
165
+ <span v-else >
166
+ <span v-for =" (colSize, index) in placeholderSizes" :key =" index"
167
+ :class =" { 'dark-mode': isDarkMode }" >
168
+ <span :class =" `placeholder col-${colSize}`" ></span >
126
169
</span >
127
170
</span >
128
- </li >
129
-
130
- <li v-show =" !isMobile || !isCardsCollapsed" class =" jn-list-group-item border-0"
131
- :class =" { 'dark-mode': isDarkMode }" >
132
- <span class =" jn-text col-auto" >
133
- <i class =" bi bi-buildings" ></i >
134
- {{ t('ipInfos.ASN') }} :  ;
135
- </span >
136
- <span v-if =" card.asnlink" class =" col-9 " >
137
- {{ card.asn }} <i v-if =" configs.cloudFlare" class =" bi bi-info-circle"
138
- @click =" getASNInfo(card.asn, index)" data-bs-toggle =" collapse"
139
- :data-bs-target =" '#' + 'collapseASNInfo-' + index" aria-expanded =" false"
140
- :aria-controls =" 'collapseASNInfo-' + index" role =" button"
141
- :aria-label =" 'Display AS Info of' + card.asn"
142
- v-tooltip =" { title: t('Tooltips.ShowASNInfo'), placement: 'right' }" ></i >
143
- </span >
144
- </li >
145
-
146
- <div class =" collapse alert alert-light placeholder-glow lh-lg fw-bold p-0"
147
- :id =" 'collapseASNInfo-' + index" :data-bs-theme =" isDarkMode ? 'dark' : ''" >
148
-
149
- <!-- 通过将 collapse 的 padding 设置为 0,然后添加一个子 div 设置 padding 的方式,避免 Bootstrap 的 collapse 发生卡顿,很奇怪的 bug -->
150
-
151
- <div class =" p-3" >
152
- <span v-if =" asnInfos[card.asn]" >
153
- <i class =" bi bi-info-circle-fill" ></i > <span class =" fw-light" >{{ t('ipInfos.ASNInfo.note')
154
- }}</span >
155
- <br />
156
- <template v-for =" (item ,key ) in asnInfos [card .asn ] " >
157
- <span class =" fw-light" >
158
- {{ t(`ipInfos.ASNInfo.${key}`) }}
159
- </span >
160
- {{ item }}
161
- <br />
162
- </template >
163
- </span >
164
-
165
- <span v-else >
166
- <span v-for =" (colSize, index) in placeholderSizes" :key =" index"
167
- :class =" { 'dark-mode': isDarkMode }" >
168
- <span :class =" `placeholder col-${colSize}`" ></span >
169
- </span >
170
- </span >
171
- </div >
172
171
</div >
173
- </ul >
174
- </div >
172
+ </div >
173
+ </ul >
174
+ </div >
175
175
176
- <div v-else class =" card-body" >
177
- <ul class =" list-group list-group-flush placeholder-glow" >
178
- <li v-for =" (colSize, index) in placeholderSizes" :key =" index" class =" list-group-item jn-list-group-item"
179
- :class =" { 'dark-mode': isDarkMode }" >
180
- <span :class =" `placeholder col-${colSize}`" ></span >
181
- </li >
182
- </ul >
176
+ <div v-else-if =" (card.ip === t('ipInfos.IPv4Error')) || (card.ip === t('ipInfos.IPv6Error'))"
177
+ class =" card-body jn-ip-error" >
178
+ <div >
179
+ <IPErrorIcon />
183
180
</div >
181
+ </div >
184
182
183
+ <div v-else class =" card-body" >
184
+ <ul class =" list-group list-group-flush placeholder-glow" >
185
+ <li v-for =" (colSize, index) in placeholderSizes" :key =" index" class =" list-group-item jn-list-group-item"
186
+ :class =" { 'dark-mode': isDarkMode }" >
187
+ <span :class =" `placeholder col-${colSize}`" ></span >
188
+ </li >
189
+ </ul >
185
190
</div >
191
+
186
192
</div >
187
193
</div >
188
194
</div >
195
+ </div >
189
196
</template >
190
197
191
198
@@ -201,6 +208,9 @@ import { getIPFromIPIP, getIPFromCloudflare_V4, getIPFromCloudflare_V6, getIPFro
201
208
202
209
const { t } = useI18n ();
203
210
211
+ // 导入 IP 错误图标
212
+ import IPErrorIcon from ' ./svgicons/IPError.vue' ;
213
+
204
214
// Store
205
215
const store = useMainStore ();
206
216
const isDarkMode = computed (() => store .isDarkMode );
@@ -470,7 +480,7 @@ const refreshCard = (card, index) => {
470
480
break ;
471
481
case 3 :
472
482
fetchIP (3 , getIPFromIPChecking64);
473
- break ;
483
+ break ;
474
484
case 4 :
475
485
fetchIP (4 , getIPFromIPChecking4);
476
486
break ;
@@ -577,6 +587,13 @@ defineExpose({
577
587
z-index : 1 ;
578
588
}
579
589
590
+ .jn-ip-error {
591
+ display : flex ;
592
+ justify-content : space-evenly ;
593
+ align-items : center ;
594
+ flex-direction : column ;
595
+ }
596
+
580
597
.dropdown-item.disabled ,
581
598
.dropdown-item :disabled {
582
599
text-decoration : line-through ;
0 commit comments