Skip to main content
Glama
LoadingOverlay.vue4.16 kB
<template> <div v-if="visible" :class="['loading-overlay', { fullscreen }]"> <div class="loading-content"> <!-- 加载动画 --> <div class="loading-spinner"> <component :is="spinnerComponent" :size="spinnerSize" /> </div> <!-- 加载文本 --> <div v-if="text" class="loading-text">{{ text }}</div> <!-- 进度条 --> <div v-if="showProgress" class="loading-progress"> <el-progress :percentage="progress" :show-text="showProgressText" :status="progressStatus" :stroke-width="4" /> <div v-if="progressText" class="progress-text">{{ progressText }}</div> </div> <!-- 详细信息 --> <div v-if="details" class="loading-details">{{ details }}</div> <!-- 取消按钮 --> <div v-if="cancelable" class="loading-actions"> <el-button size="small" @click="handleCancel">取消</el-button> </div> </div> <!-- 背景遮罩 --> <div v-if="mask" class="loading-mask" @click="handleMaskClick"></div> </div> </template> <script setup lang="ts"> import { computed } from "vue"; import { Loading, CircleCheck, CircleClose, Warning, } from "@element-plus/icons-vue"; interface Props { visible?: boolean; text?: string; fullscreen?: boolean; mask?: boolean; maskClosable?: boolean; spinner?: "loading" | "dots" | "circle" | "bars"; size?: "small" | "default" | "large"; showProgress?: boolean; progress?: number; progressText?: string; progressStatus?: "success" | "exception" | "warning" | ""; showProgressText?: boolean; details?: string; cancelable?: boolean; } interface Emits { (e: "cancel"): void; (e: "mask-click"): void; } const props = withDefaults(defineProps<Props>(), { visible: false, fullscreen: false, mask: true, maskClosable: false, spinner: "loading", size: "default", showProgress: false, progress: 0, progressStatus: "", showProgressText: true, cancelable: false, }); const emit = defineEmits<Emits>(); // 计算属性 const spinnerComponent = computed(() => { switch (props.spinner) { case "loading": return Loading; case "circle": return Loading; case "dots": return Loading; case "bars": return Loading; default: return Loading; } }); const spinnerSize = computed(() => { const sizes = { small: 24, default: 32, large: 48, }; return sizes[props.size]; }); // 事件处理 const handleCancel = () => { emit("cancel"); }; const handleMaskClick = () => { if (props.maskClosable) { emit("mask-click"); } }; </script> <style scoped> .loading-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 2000; display: flex; align-items: center; justify-content: center; } .loading-overlay.fullscreen { position: fixed; z-index: 9999; } .loading-content { position: relative; z-index: 2001; display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px; background: var(--el-bg-color); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 200px; max-width: 400px; } .loading-spinner { display: flex; align-items: center; justify-content: center; color: var(--el-color-primary); } .loading-spinner .el-icon { animation: rotate 1s linear infinite; } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .loading-text { font-size: 14px; color: var(--el-text-color-primary); text-align: center; line-height: 1.5; } .loading-progress { width: 100%; margin-top: 8px; } .progress-text { font-size: 12px; color: var(--el-text-color-secondary); text-align: center; margin-top: 8px; } .loading-details { font-size: 12px; color: var(--el-text-color-secondary); text-align: center; line-height: 1.4; max-height: 60px; overflow-y: auto; } .loading-actions { margin-top: 8px; } .loading-mask { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); z-index: 2000; } </style>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zaizaizhao/mcp-swagger-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server