<template>
<div>
<TsSkeleton />
</div>
</template>
<script setup lang="ts">
import { TsSkeleton } from 'tui';
</script>
bg 背景色来强调骨架层的区域感知。<template>
<div>
<TsSkeleton radius="50%" style="margin-bottom: 12px" width="48px" height="48px" bg="#2866c8" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 24px" width="450px" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="360px" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 24px" width="450px" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 24px" width="450px" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="24px" />
<TsSkeleton radius="5px" style="margin-bottom: 12px" width="100%" height="24px" />
</div>
</template>
<script setup lang="ts">
import { TsSkeleton } from 'tui';
</script>
用户信息卡片骨架
列表项骨架
<template>
<div class="skeleton-avatar-demo">
<div class="demo-section">
<p class="demo-title">用户信息卡片骨架</p>
<div class="user-card">
<TsSkeleton width="60px" height="60px" radius="50%" bg="#e6f7ff" />
<div class="user-info">
<TsSkeleton width="120px" height="20px" radius="4px" style="margin-bottom: 8px" />
<TsSkeleton width="80px" height="16px" radius="4px" bg="#f0f0f0" />
</div>
</div>
</div>
<div class="demo-section">
<p class="demo-title">列表项骨架</p>
<div class="list-item">
<TsSkeleton width="40px" height="40px" radius="4px" bg="#d9f7be" />
<div class="item-content">
<TsSkeleton width="60%" height="16px" radius="4px" style="margin-bottom: 6px" />
<TsSkeleton width="40%" height="14px" radius="4px" bg="#f5f5f5" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { TsSkeleton } from 'tui';
</script>
<style scoped>
.skeleton-avatar-demo {
max-width: 500px;
}
.demo-section {
margin-bottom: 24px;
}
.demo-title {
font-weight: bold;
margin-bottom: 12px;
color: var(--ts-color-text);
}
.user-card {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: #fafafa;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
.user-info {
flex: 1;
}
.list-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
background: white;
border-radius: 6px;
border: 1px solid #f0f0f0;
}
.item-content {
flex: 1;
}
</style>
表单骨架
搜索框骨架
<template>
<div class="skeleton-form-demo">
<div class="demo-section">
<p class="demo-title">表单骨架</p>
<div class="form-skeleton">
<div class="form-group">
<TsSkeleton width="80px" height="16px" radius="4px" style="margin-bottom: 8px" bg="#e8f4fd" />
<TsSkeleton width="100%" height="40px" radius="6px" style="margin-bottom: 16px" />
</div>
<div class="form-group">
<TsSkeleton width="60px" height="16px" radius="4px" style="margin-bottom: 8px" bg="#e8f4fd" />
<TsSkeleton width="100%" height="40px" radius="6px" style="margin-bottom: 16px" />
</div>
<div class="form-group">
<TsSkeleton width="100px" height="16px" radius="4px" style="margin-bottom: 8px" bg="#e8f4fd" />
<TsSkeleton width="100%" height="80px" radius="6px" style="margin-bottom: 16px" />
</div>
<div class="form-actions">
<TsSkeleton width="80px" height="36px" radius="6px" bg="#1890ff" />
<TsSkeleton width="80px" height="36px" radius="6px" bg="#f5f5f5" style="margin-left: 12px" />
</div>
</div>
</div>
<div class="demo-section">
<p class="demo-title">搜索框骨架</p>
<div class="search-skeleton">
<TsSkeleton width="100%" height="44px" radius="22px" bg="#f8f9fa" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { TsSkeleton } from 'tui';
</script>
<style scoped>
.skeleton-form-demo {
max-width: 600px;
}
.demo-section {
margin-bottom: 32px;
}
.demo-title {
font-weight: bold;
margin-bottom: 16px;
color: var(--ts-color-text);
}
.form-skeleton {
padding: 20px;
background: white;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
.form-group {
margin-bottom: 20px;
}
.form-actions {
display: flex;
justify-content: flex-end;
}
.search-skeleton {
padding: 16px;
background: white;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
</style>
文章卡片骨架
产品卡片骨架
<template>
<div class="skeleton-card-demo">
<div class="demo-section">
<p class="demo-title">文章卡片骨架</p>
<div class="card-skeleton">
<TsSkeleton width="100%" height="200px" radius="8px" bg="#f0f0f0" style="margin-bottom: 16px" />
<TsSkeleton width="70%" height="24px" radius="4px" style="margin-bottom: 8px" />
<TsSkeleton width="50%" height="18px" radius="4px" style="margin-bottom: 12px" />
<TsSkeleton width="100%" height="16px" radius="4px" style="margin-bottom: 6px" bg="#fafafa" />
<TsSkeleton width="100%" height="16px" radius="4px" style="margin-bottom: 6px" bg="#fafafa" />
<TsSkeleton width="100%" height="16px" radius="4px" bg="#fafafa" />
</div>
</div>
<div class="demo-section">
<p class="demo-title">产品卡片骨架</p>
<div class="product-grid">
<div class="product-card" v-for="i in 3" :key="i">
<TsSkeleton width="100%" height="180px" radius="8px" bg="#e8f4fd" style="margin-bottom: 12px" />
<TsSkeleton width="80%" height="20px" radius="4px" style="margin-bottom: 8px" />
<TsSkeleton width="60%" height="18px" radius="4px" bg="#ff6b6b" style="margin-bottom: 8px" />
<TsSkeleton width="100%" height="32px" radius="6px" bg="#1890ff" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { TsSkeleton } from 'tui';
</script>
<style scoped>
.skeleton-card-demo {
max-width: 800px;
}
.demo-section {
margin-bottom: 32px;
}
.demo-title {
font-weight: bold;
margin-bottom: 16px;
color: var(--ts-color-text);
}
.card-skeleton {
padding: 20px;
background: white;
border-radius: 12px;
border: 1px solid #e8e8e8;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.product-card {
padding: 16px;
background: white;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
</style>
加载状态切换
不同加载进度
<template>
<div class="skeleton-loading-demo">
<div class="demo-section">
<p class="demo-title">加载状态切换</p>
<div class="loading-controls">
<button @click="toggleLoading" class="toggle-btn">
{{ isLoading ? '显示内容' : '显示骨架屏' }}
</button>
</div>
<div class="content-container">
<!-- 骨架屏状态 -->
<div v-if="isLoading" class="skeleton-content">
<TsSkeleton width="60px" height="60px" radius="50%" bg="#e6f7ff" style="margin-bottom: 16px" />
<TsSkeleton width="100%" height="21px" radius="4px" style="margin-bottom: 10px" />
<TsSkeleton width="80%" height="21px" radius="4px" style="margin-bottom: 8px" bg="#f0f0f0" />
<TsSkeleton width="100%" height="21px" radius="4px" style="margin-bottom: 8px" bg="#f0f0f0" />
<TsSkeleton width="100%" height="21px" radius="4px" bg="#f0f0f0" />
</div>
<!-- 实际内容 -->
<div v-else class="actual-content">
<div class="user-avatar">👤</div>
<div class="user-info">
<h3 style="height: 21px;line-height: 21px;margin-bottom: 10px;">张三</h3>
<p style="height: 21px;line-height: 21px;">前端开发工程师</p>
<p style="height: 21px;line-height: 21px;">负责公司核心产品的前端开发工作,专注于用户体验优化。</p>
<p style="height: 21px;line-height: 21px;margin-bottom: 0;">熟练掌握 Vue、React、TypeScript 等技术栈。</p>
</div>
</div>
</div>
</div>
<div class="demo-section">
<p class="demo-title">不同加载进度</p>
<div class="progress-controls">
<button @click="setProgress(0.3)" class="progress-btn">30%</button>
<button @click="setProgress(0.6)" class="progress-btn">60%</button>
<button @click="setProgress(1.0)" class="progress-btn">100%</button>
</div>
<div class="progress-skeleton">
<TsSkeleton
width="100%"
height="20px"
radius="4px"
:bg="progressColor"
style="margin-bottom: 8px"
/>
<TsSkeleton
width="`${progress * 100}%`"
height="16px"
radius="4px"
bg="#1890ff"
style="margin-bottom: 6px"
/>
<TsSkeleton
width="100%"
height="16px"
radius="4px"
bg="#f0f0f0"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { TsSkeleton } from 'tui';
const isLoading = ref(true);
const progress = ref(0.3);
const progressColor = computed(() => {
if (progress.value < 0.5) return '#ff6b6b';
if (progress.value < 0.8) return '#ffa940';
return '#52c41a';
});
const toggleLoading = () => {
isLoading.value = !isLoading.value;
};
const setProgress = (value: number) => {
progress.value = value;
};
</script>
<style scoped>
.skeleton-loading-demo {
max-width: 600px;
}
.demo-section {
margin-bottom: 32px;
}
.demo-title {
font-weight: bold;
margin-bottom: 16px;
color: var(--ts-color-text);
}
.loading-controls {
margin-bottom: 16px;
}
.toggle-btn {
padding: 8px 16px;
background: #1890ff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.toggle-btn:hover {
background: #40a9ff;
}
.content-container {
padding: 20px;
background: white;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
.skeleton-content {
animation: fadeIn 0.3s ease-in-out;
font-size: 0;
}
.actual-content {
animation: fadeIn 0.3s ease-in-out;
}
.user-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
background: #e6f7ff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 16px;
}
.user-info h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #333;
}
.user-info p {
margin: 0 0 8px 0;
color: #666;
line-height: 1.5;
}
.progress-controls {
margin-bottom: 16px;
}
.progress-btn {
padding: 6px 12px;
margin-right: 8px;
background: #f0f0f0;
border: 1px solid #d9d9d9;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.progress-btn:hover {
background: #e0e0e0;
}
.progress-skeleton {
padding: 20px;
background: white;
border-radius: 8px;
border: 1px solid #e8e8e8;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>