258258 <p class =" quick-link-desc" >配置管理,保存后可重启服务</p >
259259 </div >
260260 </router-link >
261+ <router-link to =" /danmaku" target =" _blank" class =" quick-link-card" >
262+ <div class =" quick-link-icon" >
263+ <el-icon :size =" 24" ><ChatLineSquare /></el-icon >
264+ </div >
265+ <div class =" quick-link-content" >
266+ <h4 class =" quick-link-title" >弹幕小部件</h4 >
267+ <p class =" quick-link-desc" >独立的弹幕显示窗口</p >
268+ </div >
269+ </router-link >
270+ <router-link to =" /subtitle" target =" _blank" class =" quick-link-card" >
271+ <div class =" quick-link-icon" >
272+ <el-icon :size =" 24" ><ChatDotRound /></el-icon >
273+ </div >
274+ <div class =" quick-link-content" >
275+ <h4 class =" quick-link-title" >字幕小部件</h4 >
276+ <p class =" quick-link-desc" >独立的字幕显示窗口</p >
277+ </div >
278+ </router-link >
261279 </div >
262280 </section >
281+
282+ <!-- 小部件信息 -->
283+ <section class =" widget-info-section" >
284+ <div class =" section-header" >
285+ <h2 class =" section-title" >小部件访问地址</h2 >
286+ <el-button
287+ type =" primary"
288+ size =" small"
289+ text
290+ @click =" showWidgetInfo = !showWidgetInfo"
291+ >
292+ {{ showWidgetInfo ? '收起' : '展开' }}
293+ <el-icon :class =" { 'is-rotate': showWidgetInfo }" >
294+ <ArrowDown />
295+ </el-icon >
296+ </el-button >
297+ </div >
298+ <el-collapse-transition >
299+ <div v-show =" showWidgetInfo" class =" widget-urls" >
300+ <div class =" widget-url-card" >
301+ <div class =" widget-url-info" >
302+ <span class =" widget-url-label" >弹幕小部件</span >
303+ <code class =" widget-url-value" >{{ baseUrl }}/danmaku</code >
304+ </div >
305+ <el-button
306+ class =" copy-btn"
307+ type =" primary"
308+ size =" small"
309+ text
310+ @click =" copyUrl(`${baseUrl}/danmaku`)"
311+ >
312+ <el-icon ><CopyDocument /></el-icon >
313+ 复制
314+ </el-button >
315+ </div >
316+ <div class =" widget-url-card" >
317+ <div class =" widget-url-info" >
318+ <span class =" widget-url-label" >字幕小部件</span >
319+ <code class =" widget-url-value" >{{ baseUrl }}/subtitle</code >
320+ </div >
321+ <el-button
322+ class =" copy-btn"
323+ type =" primary"
324+ size =" small"
325+ text
326+ @click =" copyUrl(`${baseUrl}/subtitle`)"
327+ >
328+ <el-icon ><CopyDocument /></el-icon >
329+ 复制
330+ </el-button >
331+ </div >
332+ </div >
333+ </el-collapse-transition >
334+ </section >
263335 </div >
264336</template >
265337
266338<script setup lang="ts">
267- import { ref , onMounted , onUnmounted } from ' vue' ;
268- import { Document , Connection , Tools , Setting } from ' @element-plus/icons-vue' ;
339+ import { ref , computed , onMounted , onUnmounted } from ' vue' ;
340+ import { Document , Connection , Tools , Setting , ChatLineSquare , ChatDotRound , CopyDocument , ArrowDown } from ' @element-plus/icons-vue' ;
269341import { ElMessage } from ' element-plus' ;
270342import { useSystemStore , useProvidersStore } from ' @/stores' ;
271343import { storeToRefs } from ' pinia' ;
@@ -282,6 +354,21 @@ const domainLoading = ref<Record<string, 'start' | 'stop' | null>>({
282354 output: null ,
283355});
284356
357+ // Widget info visibility
358+ const showWidgetInfo = ref (false );
359+
360+ // Base URL for widget links
361+ const baseUrl = computed (() => window .location .origin );
362+
363+ // Copy widget URL to clipboard
364+ function copyUrl(url : string ) {
365+ navigator .clipboard .writeText (url ).then (() => {
366+ ElMessage .success (' URL 已复制到剪贴板' );
367+ }).catch (() => {
368+ ElMessage .error (' 复制失败' );
369+ });
370+ }
371+
285372// Get providers for a specific domain
286373function getDomainProviders(domain : string ) {
287374 if (! providers .value ) return [];
@@ -759,6 +846,56 @@ onUnmounted(() => {
759846 line-height : 1.4 ;
760847}
761848
849+ /* 小部件信息区域 */
850+ .widget-info-section {
851+ margin-bottom : var (--spacing-lg );
852+ }
853+
854+ .widget-urls {
855+ display : flex ;
856+ flex-direction : column ;
857+ gap : var (--spacing-md );
858+ }
859+
860+ .widget-url-card {
861+ display : flex ;
862+ align-items : center ;
863+ justify-content : space-between ;
864+ padding : var (--spacing-md );
865+ background : var (--bg-card );
866+ border-radius : var (--radius-md );
867+ border : 1px solid var (--border-color-light );
868+ }
869+
870+ .widget-url-info {
871+ display : flex ;
872+ align-items : center ;
873+ gap : var (--spacing-md );
874+ }
875+
876+ .widget-url-label {
877+ font-size : 14px ;
878+ font-weight : 500 ;
879+ color : var (--text-primary );
880+ }
881+
882+ .widget-url-value {
883+ font-size : 13px ;
884+ font-family : var (--font-mono );
885+ color : var (--text-secondary );
886+ background : var (--bg-hover );
887+ padding : 4px 8px ;
888+ border-radius : var (--radius-sm );
889+ }
890+
891+ .copy-btn {
892+ flex-shrink : 0 ;
893+ }
894+
895+ .is-rotate {
896+ transform : rotate (180deg );
897+ }
898+
762899/* Utility Classes */
763900.mono {
764901 font-family : var (--font-mono );
0 commit comments