登录页功能完成
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div
|
||||
class="skeleton-container"
|
||||
:style="containerStyle"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in skeletonItems"
|
||||
:key="index"
|
||||
class="skeleton-item"
|
||||
:style="getItemStyle(item)"
|
||||
>
|
||||
<!-- 内部内容根据类型渲染 -->
|
||||
<slot v-if="item.type === 'custom' && index === 0" :item="item" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, toRefs } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
// 定义骨架屏项目类型
|
||||
interface SkeletonItem {
|
||||
type?: 'rect' | 'circle' | 'custom' // 形状类型
|
||||
width?: string | number // 宽度
|
||||
height?: string | number // 高度
|
||||
radius?: string | number // 圆角半径,仅rect类型有效
|
||||
margin?: string | number // 间距
|
||||
animation?: boolean // 是否启用动画
|
||||
}
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
// 骨架屏项目配置
|
||||
items: {
|
||||
type: Array as PropType<SkeletonItem[]>,
|
||||
default: () => [
|
||||
{ type: 'rect', width: '100%', height: '200px', animation: true },
|
||||
{ type: 'rect', width: '80%', height: '30px', margin: '15px 0', animation: true },
|
||||
{ type: 'rect', width: '60%', height: '30px', margin: '15px 0', animation: true }
|
||||
]
|
||||
},
|
||||
// 骨架屏主色调
|
||||
baseColor: {
|
||||
type: String,
|
||||
default: '#f2f2f2'
|
||||
},
|
||||
// 骨架屏高光色
|
||||
highlightColor: {
|
||||
type: String,
|
||||
default: '#e0e0e0'
|
||||
},
|
||||
// 是否显示骨架屏
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
// 计算容器样式
|
||||
const containerStyle = computed(() => {
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start'
|
||||
}
|
||||
})
|
||||
|
||||
// 获取每个骨架屏项目的样式
|
||||
const getItemStyle = (item: SkeletonItem) => {
|
||||
const style: Record<string, string | number> = {
|
||||
background: props.baseColor,
|
||||
margin: item.margin || 0,
|
||||
animation: props.show && item.animation
|
||||
? 'skeleton-loading 1.5s infinite'
|
||||
: 'none'
|
||||
}
|
||||
|
||||
// 根据类型设置不同样式
|
||||
if (item.type === 'rect') {
|
||||
style.width = item.width || '100%'
|
||||
style.height = item.height || '30px'
|
||||
style.borderRadius = item.radius || '4px'
|
||||
} else if (item.type === 'circle') {
|
||||
style.width = item.width || item.height || '30px'
|
||||
style.height = item.height || item.width || '30px'
|
||||
style.borderRadius = '50%'
|
||||
} else if (item.type === 'custom') {
|
||||
// 自定义类型由插槽处理
|
||||
}
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
// 注册骨架屏动画
|
||||
const style = document.createElement('style')
|
||||
style.innerHTML = `
|
||||
@keyframes skeleton-loading {
|
||||
0% { background-position: -468px 0 }
|
||||
100% { background-position: 468px 0 }
|
||||
}
|
||||
.skeleton-item {
|
||||
background: linear-gradient(90deg,
|
||||
${props.baseColor} 25%,
|
||||
${props.highlightColor} 50%,
|
||||
${props.baseColor} 75%
|
||||
);
|
||||
background-size: 468px 104px;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.skeleton-container {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user