This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
@import '../avatar/index';
/* stylelint-disable selector-class-pattern */
.nut-skeleton {
position: relative;
display: inline-block;
overflow: hidden;
vertical-align: middle;
.nut-skeleton-content {
display: flex;
.avatarClass {
margin-right: 20px;
background-color: $skeleton-content-avatar-background-color;
}
.nut-skeleton-content__line {
display: flex;
flex-direction: column;
.nut-skeleton-blockTitle,
.nut-skeleton-blockLine {
width: 100%;
margin-bottom: 10px;
background-color: $skeleton-content-line-background-color;
}
.nut-skeleton-blockTitle {
width: 30%;
}
.blockLine ~ .blockLine:last-of-type {
width: 70%;
}
.nut-skeleton-blockTitle:last-of-type,
.nut-skeleton-blockLine:last-of-type {
margin-bottom: 0;
}
.nut-skeleton-blockTitle--round,
.nut-skeleton-blockLine--round {
border-radius: 10px;
}
}
}
.nut-skeleton-animation {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
background: $skeleton-animation-background-color;
background-repeat: no-repeat;
animation: backpos 2s ease-in-out 0s infinite;
}
@keyframes backpos {
0% {
background-position-x: -500px;
}
100% {
background-position-x: calc(500px + 100%);
}
}
}

View File

@@ -0,0 +1 @@
export type * from './skeleton'

View File

@@ -0,0 +1,49 @@
import type { ExtractPropTypes } from 'vue'
import { commonProps, makeStringProp, truthProp } from '../_utils'
import type { AvatarShape } from '../avatar/type'
export const skeletonProps = {
...commonProps,
/**
* @description 是否显示骨架屏
*/
loading: truthProp,
/**
* @description 每行宽度
*/
width: makeStringProp('100px'),
/**
* @description 每行高度
*/
height: makeStringProp('15px'),
/**
* @description 是否开启骨架屏动画
*/
animated: Boolean,
/**
* @description 是否显示头像
*/
avatar: Boolean,
/**
* @description 头像形状
*/
avatarShape: makeStringProp<AvatarShape>('round'),
/**
* @description 头像大小
*/
avatarSize: makeStringProp('50px'),
/**
* @description 标题/段落是否采用圆角风格
*/
round: Boolean,
/**
* @description 设置段落行数
*/
row: makeStringProp('1'),
/**
* @description 是否显示段落标题
*/
title: truthProp,
}
export type SkeletonProps = ExtractPropTypes<typeof skeletonProps>

View File

@@ -0,0 +1,85 @@
<script setup lang="ts">
import { computed, defineComponent, toRefs } from 'vue'
import { PREFIX } from '../_constants'
import { getMainClass } from '../_utils'
import NutAvatar from '../avatar/avatar.vue'
import { skeletonProps } from './skeleton'
const props = defineProps(skeletonProps)
const { avatarShape, round, avatarSize } = toRefs(props)
const classes = computed(() => {
return getMainClass(props, componentName)
})
const avatarClass = computed(() => {
const prefixCls = 'avatarClass'
return {
[prefixCls]: true,
[`${prefixCls}--${avatarShape.value}`]: avatarShape.value,
}
})
function getBlockClass(prefixCls: string) {
return {
[prefixCls]: true,
[`${prefixCls}--round`]: round.value,
}
}
function getStyle(): import('vue').CSSProperties {
if (avatarSize.value) {
return {
width: avatarSize.value,
height: avatarSize.value,
}
}
return {
width: '50px',
height: '50px',
}
}
</script>
<script lang="ts">
const componentName = `${PREFIX}-skeleton`
export default defineComponent({
name: componentName,
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
})
</script>
<template>
<view v-if="!loading">
<slot />
</view>
<view v-else :class="classes" :style="customStyle">
<view v-if="animated" class="nut-skeleton-animation" />
<view class="nut-skeleton-content">
<NutAvatar
v-if="avatar"
:custom-class="avatarClass"
:shape="avatarShape"
:custom-style="getStyle()"
/>
<view class="nut-skeleton-content__line" :style="{ width }">
<view v-if="title" :class="getBlockClass('nut-skeleton-blockTitle')" :style="{ height }" />
<view
v-for="_ in Number(row)"
:key="_"
:class="getBlockClass('nut-skeleton-blockLine')"
:style="{ height }"
/>
</view>
</view>
</view>
</template>
<style lang="scss">
@import './index';
</style>