44
44
</div >
45
45
</div >
46
46
47
+ <Transition name =" slide-fade" >
48
+ <div class =" d-flex align-items-center align-content-center justify-content-end pb-2"
49
+ :data-bs-theme =" isDarkMode ? 'dark' : ''" v-if =" speedTestStatus !== 'idle' && connectionData.colo" >
50
+ <div >
51
+ <i class =" bi bi-person-arms-up" ></i >
52
+ {{connectionData.country}}
53
+ <span v-if =" connectionData.country" :class =" 'jn-fl fi fi-' + connectionData.loc.toLowerCase()" ></span >
54
+ </div >
55
+ <div class =" mx-2" >
56
+ <i class =" bi bi-arrow-left-right" ></i >
57
+ </div >
58
+ <div >
59
+ <i class =" bi bi-globe" ></i >
60
+ {{connectionData.colo}},  ;
61
+ {{connectionData.coloCountry}} <span v-if =" connectionData.coloCountry"
62
+ :class =" 'jn-fl fi fi-' + connectionData.coloCountryCode.toLowerCase()" ></span >
63
+ </div >
64
+ </div >
65
+ </Transition >
47
66
<div class =" progress" style =" height : 20px ; margin : 4pt 0 20pt 0 ;"
48
67
:class =" { 'jn-opacity-0': speedTestStatus == 'idle', 'jn-progress-dark': isDarkMode }" >
49
68
<div class =" progress-bar progress-bar-striped jn-progress"
84
103
</div >
85
104
<div class =" row alert alert-success m-1 p-2 " :data-bs-theme =" isDarkMode ? 'dark' : ''"
86
105
v-if =" speedTestStatus === 'finished' && hasScores" >
87
- <p id =" score" class =" speedtest-p" ><i class =" bi bi-calendar2-check" ></i > {{ t('speedtest.score') }}
106
+ <p id =" score" class =" speedtest-p" ><i class =" bi bi-calendar2-check" ></i >  ;
107
+ <span v-if =" connectionData.colo" >
108
+ {{ t('speedtest.connectionFrom') }}
109
+ {{ connectionData.ip }} ( {{ connectionData.country }} )
110
+ {{ t('speedtest.connectionTo') }}
111
+ {{ connectionData.colo }}
112
+ ( {{ connectionData.coloCity }}
113
+ , {{ connectionData.coloCountry }} )
114
+ {{ t('speedtest.connectionEnd') }}
115
+ </span >
116
+ {{ t('speedtest.score') }}
88
117
{{ t('speedtest.videoStreaming') }}
89
118
<span :class =" speedTest.streamingScore >= 50 ? 'text-success' : 'jn-text-warning'" >
90
119
{{ speedTest.streamingScore }}
@@ -113,6 +142,9 @@ import { ref, computed, onMounted, reactive, markRaw } from 'vue';
113
142
import { useMainStore } from ' @/store' ;
114
143
import { useI18n } from ' vue-i18n' ;
115
144
import { trackEvent } from ' @/utils/use-analytics' ;
145
+ import { isValidIP } from ' @/utils/valid-ip.js' ;
146
+ import getCountryName from ' @/utils/country-name.js' ;
147
+ import getColoCountry from ' @/utils/speedtest-colos.js' ;
116
148
// 引入 SpeedTest
117
149
import SpeedTestEngine from ' @cloudflare/speedtest' ;
118
150
@@ -121,6 +153,7 @@ const { t } = useI18n();
121
153
const store = useMainStore ();
122
154
const isDarkMode = computed (() => store .isDarkMode );
123
155
const isMobile = computed (() => store .isMobile );
156
+ const lang = computed (() => store .lang );
124
157
125
158
const speedTest = reactive ({
126
159
id: " speedTest" ,
@@ -147,6 +180,40 @@ const packageSize = reactive({
147
180
}
148
181
});
149
182
183
+ const connectionData = ref ({
184
+ ip: " " ,
185
+ colo: " " ,
186
+ loc: " " ,
187
+ country: " " ,
188
+ coloCountry: " " ,
189
+ coloCountryCode: " " ,
190
+ coloCity: " "
191
+ });
192
+
193
+ const getIPFromSpeedTest = async () => {
194
+ try {
195
+ const response = await fetch (" https://speed.cloudflare.com/cdn-cgi/trace" );
196
+ const data = await response .text ();
197
+ const lines = data .split (" \n " );
198
+
199
+ const ip = lines .find ((line ) => line .startsWith (" ip=" ))? .split (" =" )[1 ];
200
+ const colo = lines .find ((line ) => line .startsWith (" colo=" ))? .split (" =" )[1 ];
201
+ const loc = lines .find ((line ) => line .startsWith (" loc=" ))? .split (" =" )[1 ];
202
+
203
+ if (isValidIP (ip)) {
204
+ const country = getCountryName (loc, lang .value ) || ' ' ;
205
+ const coloCountryCode = getColoCountry (colo).country || ' ' ;
206
+ const coloCity = getColoCountry (colo).city || ' ' ;
207
+ const coloCountry = getCountryName (coloCountryCode, lang .value ) || ' ' ;
208
+ return { ip, colo, loc, country, coloCountry, coloCountryCode, coloCity };
209
+ } else {
210
+ console .error (" Invalid IP from SpeedTest Server:" , ip);
211
+ }
212
+ } catch (error) {
213
+ console .error (" Error fetching IP from SpeedTest Server:" , error);
214
+ }
215
+ };
216
+
150
217
// 定义 Speed Test 引擎
151
218
let testEngine;
152
219
@@ -166,12 +233,15 @@ const resetSpeedTest = () => {
166
233
};
167
234
168
235
// Speed Test 引擎
169
- const speedTestController = () => {
236
+ const speedTestController = async () => {
170
237
if (speedTestStatus .value === ' running' ) {
171
238
testEngine .pause ();
172
239
speedTestStatus .value = " paused" ;
173
240
} else {
174
241
startSpeedTest ();
242
+ if (! connectionData .value .ip ) {
243
+ connectionData .value = await getIPFromSpeedTest ();
244
+ }
175
245
}
176
246
};
177
247
@@ -365,8 +435,23 @@ defineExpose({
365
435
background- color: var (-- bs- btn- hover- bg);
366
436
border- color: var (-- bs- btn- hover- border- color);
367
437
}
438
+
368
439
.jn - text- warning {
369
440
-- bs- text- opacity: 1 ;
370
441
color: #c67c14;
371
442
}
443
+
444
+ .slide - fade- enter- active {
445
+ transition: all 0 .3s ease- out;
446
+ }
447
+
448
+ .slide - fade- leave- active {
449
+ transition: all 0 .8s cubic- bezier (1 , 0.5 , 0.8 , 1 );
450
+ }
451
+
452
+ .slide - fade- enter- from,
453
+ .slide - fade- leave- to {
454
+ transform: translateX (20px );
455
+ opacity: 0 ;
456
+ }
372
457
< / style>
0 commit comments