113113 </div >
114114 </div >
115115
116- <!-- 详情模块(精简版) -->
117- <div class =" q-card p-6" >
118- <div class =" grid grid-cols-1 md:grid-cols-3 gap-6 items-start" >
119- <div >
120- <div class =" q-muted text-[13px]" >短码</div >
121- <div class =" mt-1 q-strong" >{{ shortCode }}</div >
122- </div >
123- <div >
124- <div class =" q-muted text-[13px]" >短链</div >
125- <a :href =" shortUrl" target =" _blank" class =" mt-1 q-link" >{{ shortLabel }}</a >
126- </div >
127- <div >
128- <div class =" q-muted text-[13px]" >创建时间</div >
129- <div class =" mt-1 q-strong" >{{ formatDate(overview?.createdAt) }}</div >
130- </div >
131- <div >
132- <div class =" q-muted text-[13px]" >总访问量</div >
133- <div class =" mt-1 q-nums" >{{ overview?.totalVisits ?? '-' }}</div >
134- </div >
135- <div >
136- <div class =" q-muted text-[13px]" >今日访问量</div >
137- <div class =" mt-1 q-nums" >{{ overview?.todayVisits ?? '-' }}</div >
116+ <!-- 关键指标卡片 -->
117+ <div class =" grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4" >
118+ <div class =" stat-card" >
119+ <div class =" stat-label" >总访问量</div >
120+ <div class =" stat-value" >{{ overview?.totalVisits ?? '-' }}</div >
121+ <div class =" stat-unit" >次</div >
122+ </div >
123+ <div class =" stat-card" >
124+ <div class =" stat-label" >今日访问量</div >
125+ <div class =" stat-value" >{{ overview?.todayVisits ?? '-' }}</div >
126+ <div class =" stat-unit" >次</div >
127+ </div >
128+ <div class =" stat-card" >
129+ <div class =" stat-label" >创建时间</div >
130+ <div class =" stat-value-text" >{{ formatDate(overview?.createdAt) }}</div >
131+ </div >
132+ <div class =" stat-card" >
133+ <div class =" stat-label" >访问转化</div >
134+ <div class =" stat-value" >{{ overview?.totalVisits ? Math.round((overview.todayVisits / overview.totalVisits) * 100) : 0 }}%</div >
135+ <div class =" stat-unit" >今日占比</div >
136+ </div >
137+ </div >
138+
139+ <!-- 短链详情与Top城市 -->
140+ <div class =" grid grid-cols-1 md:grid-cols-3 gap-6" >
141+ <div class =" col-span-1 md:col-span-2" >
142+ <div class =" q-card p-6" >
143+ <div class =" q-card-title mb-4" >短链信息</div >
144+ <div class =" space-y-4" >
145+ <div class =" detail-row" >
146+ <span class =" detail-label" >短码</span >
147+ <code class =" detail-value code-block" >{{ shortCode }}</code >
148+ </div >
149+ <div class =" detail-row" >
150+ <span class =" detail-label" >短链地址</span >
151+ <a :href =" shortUrl" target =" _blank" class =" detail-value link-style" >{{ shortLabel }}</a >
152+ </div >
153+ <div class =" detail-row" >
154+ <span class =" detail-label" >原始链接</span >
155+ <div class =" mt-2 text-xs font-mono bg-gray-100 p-2 rounded break-all text-gray-600" >{{ overview?.longUrl || '-' }}</div >
156+ </div >
157+ </div >
138158 </div >
139- <div >
140- <div class =" q-muted text-[13px]" >Top 城市</div >
141- <div class =" mt-2 space-y-1" >
142- <div v-for =" c in cityData" :key =" c.label" class =" flex items-center justify-between" >
143- <span class =" q-muted" >{{ c.label }}</span >
144- <span class =" q-nums" >{{ c.value }}</span >
159+ </div >
160+ <div >
161+ <div class =" q-card p-6" >
162+ <div class =" q-card-title mb-4" >城市 Top 5</div >
163+ <div class =" space-y-2" >
164+ <div v-for =" (c, idx) in cityData" :key =" c.label" class =" city-item" >
165+ <div class =" flex items-center justify-between" >
166+ <span class =" city-rank" >{{ idx + 1 }}</span >
167+ <span class =" city-name flex-1 ml-3" >{{ c.label }}</span >
168+ <span class =" city-count" >{{ c.value }}</span >
169+ </div >
170+ <div class =" city-bar" >
171+ <div class =" city-bar-fill" :style =" { width: (c.value / (cityData[0]?.value || 1) * 100) + '%' }" ></div >
172+ </div >
145173 </div >
146- <div v-if =" !cityData.length" class =" q-muted " >{{ $t('common.noData') }}</div >
174+ <div v-if =" !cityData.length" class =" text-center py-4 text-gray-400 " >{{ $t('common.noData') }}</div >
147175 </div >
148176 </div >
149177 </div >
@@ -296,6 +324,135 @@ svg { width: 20px; height: 20px; }
296324.tf - modal { width: 680px ; max- width: 92vw ; border- radius: 16px ; padding: 24px ; background: linear- gradient (135deg ,#ffffff 0 % , #f3f7ff 100 % ); box- shadow: 0 12px 40px rgba (37 ,99 ,235 ,0.22 ); border: 1px solid rgba (37 ,99 ,235 ,0.18 ); }
297325.tf - modal- header { font- size: 16px ; font- weight: 600 ; color: #2563EB ; margin- bottom: 16px ; }
298326
327+ /* 统计卡片窗口 */
328+ .stat - card {
329+ background: linear- gradient (135deg , #ffffff 0 % , #f8fafc 100 % );
330+ border: 1px solid rgba (226 , 232 , 240 , 0.6 );
331+ border- radius: 12px ;
332+ padding: 20px ;
333+ transition: all 0 .3s ease;
334+ box- shadow: 0 2px 8px rgba (0 , 0 , 0 , 0.04 );
335+ }
336+ .stat - card: hover {
337+ transform: translateY (- 2px );
338+ box- shadow: 0 8px 16px rgba (37 , 99 , 235 , 0.15 );
339+ border- color: rgba (37 , 99 , 235 , 0.3 );
340+ }
341+ .stat - label {
342+ font- size: 13px ;
343+ color: #64748b ;
344+ font- weight: 500 ;
345+ margin- bottom: 8px ;
346+ text- transform: uppercase;
347+ letter- spacing: 0 .5px ;
348+ }
349+ .stat - value {
350+ font- size: 32px ;
351+ font- weight: 700 ;
352+ color: #1e293b ;
353+ line- height: 1 ;
354+ margin- bottom: 4px ;
355+ }
356+ .stat - value- text {
357+ font- size: 14px ;
358+ color: #334155 ;
359+ font- weight: 500 ;
360+ word- break: break - all;
361+ }
362+ .stat - unit {
363+ font- size: 12px ;
364+ color: #94a3b8 ;
365+ font- weight: 400 ;
366+ }
367+
368+ /* 详情行样式 */
369+ .detail - row {
370+ display: flex;
371+ align- items: flex- start;
372+ gap: 16px ;
373+ padding: 12px 0 ;
374+ border- bottom: 1px solid #f1f5f9;
375+ }
376+ .detail - row: last- child {
377+ border- bottom: none;
378+ }
379+ .detail - label {
380+ min- width: 80px ;
381+ font- size: 13px ;
382+ color: #64748b ;
383+ font- weight: 500 ;
384+ flex- shrink: 0 ;
385+ }
386+ .detail - value {
387+ font- size: 14px ;
388+ color: #1e293b ;
389+ flex: 1 ;
390+ word- break: break - all;
391+ }
392+ .code - block {
393+ background: #f1f5f9;
394+ padding: 6px 10px ;
395+ border- radius: 6px ;
396+ color: #2563eb ;
397+ font- weight: 500 ;
398+ font- size: 13px ;
399+ }
400+ .link - style {
401+ color: #2563eb ;
402+ text- decoration: none;
403+ border- bottom: 1px dashed #2563eb ;
404+ transition: all 0 .2s ease;
405+ }
406+ .link - style: hover {
407+ color: #1d4ed8 ;
408+ border- bottom- color: #1d4ed8 ;
409+ }
410+
411+ /* 城市接流样式 */
412+ .city - item {
413+ padding: 8px 0 ;
414+ }
415+ .city - rank {
416+ display: inline- flex;
417+ align- items: center;
418+ justify- content: center;
419+ width: 24px ;
420+ height: 24px ;
421+ border- radius: 50 % ;
422+ background: linear- gradient (135deg , #2563eb , #1d4ed8 );
423+ color: white;
424+ font- size: 12px ;
425+ font- weight: 600 ;
426+ flex- shrink: 0 ;
427+ }
428+ .city - name {
429+ font- size: 13px ;
430+ color: #334155 ;
431+ font- weight: 500 ;
432+ }
433+ .city - count {
434+ font- size: 13px ;
435+ color: #2563eb ;
436+ font- weight: 600 ;
437+ min- width: 40px ;
438+ text- align: right;
439+ flex- shrink: 0 ;
440+ }
441+ .city - bar {
442+ width: 100 % ;
443+ height: 4px ;
444+ background: #e2e8f0;
445+ border- radius: 2px ;
446+ margin- top: 4px ;
447+ overflow: hidden;
448+ }
449+ .city - bar- fill {
450+ height: 100 % ;
451+ background: linear- gradient (90deg , #2563eb , #3b82f6 );
452+ border- radius: 2px ;
453+ transition: width 0 .3s ease;
454+ }
455+
299456.fx - btn { font- family: Arial, Helvetica, sans- serif; font- weight: bold; color: #0F172A ; background- color: #F8FAFC ; padding: 0 .75em 1 .4em ; border: 1px solid rgba (148 ,163 ,184 ,0.6 ); border- radius: 0 .6rem ; position: relative; cursor: pointer; overflow: hidden; display: inline- flex; align- items: center; justify- content: center; }
300457.fx - btn span: not (.btn - text ) { position: absolute; left: 50 % ; top: 50 % ; transform: translate (- 50 % , - 50 % ); height: 26px ; width: 26px ; background- color: #2563EB ; border- radius: 50 % ; transition: .6s ease; }
301458.fx - btn span: nth- child (1 ) { transform: translate (- 3 .3em , - 4em ); }
@@ -309,9 +466,4 @@ svg { width: 20px; height: 20px; }
309466.fx - gray { background- color: #EFF6FF ; }
310467.fx - sm { padding: 0 .4em 0 .9em ; }
311468.fx - sm span: not (.btn - text ) { height: 18px ; width: 18px ; }
312- < / style>
313- function selectDays (d ){ selectedDays .value = d; refreshTrend () }
314- watch (selectedDays, async () => {
315- await refreshTrend ()
316- await refreshAll ()
317- })
469+ < / style>
0 commit comments