diff --git a/devui/skeleton/src/skeleton-types.ts b/devui/skeleton/src/skeleton-types.ts index 5774b4917b85cf5c7337e5aaa04ce1adbe7fcd89..d36ff283190f1fe6b346c7eb2361a24d65ca5419 100644 --- a/devui/skeleton/src/skeleton-types.ts +++ b/devui/skeleton/src/skeleton-types.ts @@ -1,14 +1,21 @@ -import type { ExtractPropTypes } from 'vue' +import type { ExtractPropTypes,PropType } from 'vue' + +export type ModelValue = number | string +export type ArrayModelValue = number | string | Array export const skeletonProps = { row: { - type: Number || String, + type: Number, default: 0 }, animate:{ type: Boolean, default: true }, + round:{ + type: Boolean, + default: false + }, loading:{ type: Boolean, default: true @@ -16,6 +23,30 @@ export const skeletonProps = { avatar:{ type: Boolean, default: false + }, + title:{ + type: Boolean, + default: true + }, + paragraph:{ + type: Boolean, + default: true + }, + avatarSize:{ + type: [String,Number] as PropType, + default: '40px' + }, + avatarShape:{ + type: String, + default: 'round' + }, + titleWidth:{ + type: [String,Number] as PropType, + default: '40%' + }, + rowWidth:{ + type: [String,Number,Array] as PropType, + default: '1' } } as const diff --git a/devui/skeleton/src/skeleton.scss b/devui/skeleton/src/skeleton.scss index 74508318b30df6033d918fc501efae17dbced4e5..39c952314389e0878e7d1f06226122f5a9068c7e 100644 --- a/devui/skeleton/src/skeleton.scss +++ b/devui/skeleton/src/skeleton.scss @@ -1,3 +1,5 @@ +@import '../../styles-var/devui-var.scss'; + .devui-skeleton { display: flex; justify-content: space-between; @@ -6,38 +8,48 @@ display: flex; flex: 1; justify-content: center; + padding-right: 16px; .avatar { width: 40px; height: 40px; - background-color: #cccccc; - border-radius: 50%; + background-color: #f2f2f2; } } .devui-skeleton__item__group { - flex: 7; + flex: 11; - .devui-skeleton__item { + .devui-skeleton__item, + .devui-skeleton__title { width: 100%; height: 16px; - background-color: #cccccc; - display: inline-block; - border-radius: 4px; + background-color: #f2f2f2; + } + + .devui-skeleton__title { + margin-top: 24px; } - .devui-skeleton__item:first-child { - width: 33%; + .devui-skeleton__paragraph { + margin-top: 12px; } .devui-skeleton__item:last-child { - width: 61%; + width: 60%; } } } +.devui-skeleton-animated > .devui-skeleton__item__group > .devui-skeleton__title, .devui-skeleton-animated > .devui-skeleton__avatar > .avatar, -.devui-skeleton-animated > .devui-skeleton__item__group > .devui-skeleton__item { +.devui-skeleton-animated > .devui-skeleton__item__group > div > .devui-skeleton__item { + @keyframes skeletonLoading { + to { + background-position-x: -20%; + } + } + background: linear-gradient( 100deg, @@ -45,18 +57,17 @@ rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0) 60% ) - #cccccc; + #f2f2f2; background-size: 200% 100%; background-position-x: 180%; - animation: 2s loading ease-in-out infinite; + animation: 2s skeletonLoading ease-in-out infinite; } -.devui-skeleton-animated > .devui-skeleton__avatar > .avatar { - animation-delay: 0.1s; +.devui-skeleton__avatar > .avatar, +.devui-skeleton__item__group > div > .devui-skeleton__item { + margin-top: 12px; } -@keyframes loading { - to { - background-position-x: -20%; - } +.devui-skeleton-animated > .devui-skeleton__avatar > .avatar { + animation-delay: 0.1s; } diff --git a/devui/skeleton/src/skeleton.tsx b/devui/skeleton/src/skeleton.tsx index 1939a776162f02f7b9691328fc83e4edf03e09d1..b90a2a95fba67587fc552e49a5e23365b38e7713 100644 --- a/devui/skeleton/src/skeleton.tsx +++ b/devui/skeleton/src/skeleton.tsx @@ -8,22 +8,98 @@ export default defineComponent({ props: skeletonProps, setup(props: SkeletonProps, ctx) { const { slots } = ctx; - const arr = [] - for (let index = 0; index < props.row; index++) { - arr.push(1) + + function renderAnimate(isAnimated) { + return isAnimated ? 'devui-skeleton-animated' : '' + } + function renderBorderRadius(isRound) { + return isRound ? 'border-radius: 1em;' : '' + } + function renderParagraph(isShown, rowNum, rowWidth, round) { + const arr = [] + + function pushIntoArray(type) { + for (let index = 0; index < rowNum; index++) { + arr.push({ width: type }) + } + } + (function handleRowWidth() { + if (rowWidth instanceof Array) { + for (let index = 0; index < rowNum; index++) { + if (rowWidth[index]) { + switch (typeof rowWidth[index]) { + case 'string': + arr.push({ width: rowWidth[index] }) + break + case 'number': + arr.push({ width: `${rowWidth[index]}px` }) + } + } else { + arr.push({ width: 1 }) + } + } + } else { + switch (typeof rowWidth) { + case 'string': + pushIntoArray(rowWidth) + break + case 'number': + pushIntoArray(`${rowWidth}px`) + break + } + } + })() + + return
{ + arr.map(item => { + return
+ }) + }
} + function renderAvatarStyle(avatarSize, avatarShape) { + function renderAvatarSize(avatarSize) { + switch (typeof avatarSize) { + case 'string': + return `width:${avatarSize};height:${avatarSize};` + case 'number': + return `width:${avatarSize}px;height:${avatarSize}px;` + } + } + function renderAvatarShape(avatarShape) { + return avatarShape === 'square' ? '' : 'border-radius:50%;' + } + + return (renderAvatarSize(avatarSize) + renderAvatarShape(avatarShape)) + } + function renderTitle(isVisible, titleWidth, isRound) { + function renderTitleWidth(titleWidth) { + switch (typeof titleWidth) { + case 'string': + return `width: ${titleWidth};` + case 'number': + return `width: ${titleWidth}px;` + } + } + function renderTitleVisibility(isVisible) { + return isVisible ? null : 'visibility: hidden;' + } + + return (renderTitleWidth(titleWidth) + renderBorderRadius(isRound) + renderTitleVisibility(isVisible)) + } + return () => { if (props.loading) { - return
- {props.avatar ?
: null} -
{ - arr.map(() => { - return
- }) - }
+ return
+
+
+
+
+
+ {renderParagraph(props.paragraph, props.row, props.rowWidth, props.round)} +
} - return
{slots.default?.()} + return
{slots.default?.()}
} } diff --git a/docs/components/skeleton/index.md b/docs/components/skeleton/index.md index f4d8d530f056cd29e3df8e0c447397e39ba7d9b7..8d897e1b6a1e2bb7e2363ebae0f635d7230d7b5e 100644 --- a/docs/components/skeleton/index.md +++ b/docs/components/skeleton/index.md @@ -5,44 +5,59 @@ 在需要等待加载内容的位置设置一个骨架屏,某些场景下比 Loading 的视觉效果更好。 ### 基本用法 -基础的骨架效果, 通过 `row` 属性配置占位段落行数。 +最基本的占位效果。 :::demo ```vue ``` ::: -### 显示头像 -通过 avatar 属性显示头像占位图。 +### 复杂组合 :::demo ```vue -``` -::: - - -### 展示子组件 -将 loading 属性设置成 false 表示内容加载完成,此时会隐藏占位图,并显示 Skeleton 的子组件。 - -:::demo - -```vue - @@ -51,23 +66,68 @@ import { defineComponent, ref } from 'vue' export default defineComponent({ setup () { - const loading = ref(false) + const loading = ref(true) + const animate = ref(true) + const avatar = ref(true) + const title = ref(true) + const paragraph = ref(true) + const roundAvatar = ref(true) + const round = ref(false) return { loading, + animate, + avatar, + title, + paragraph, + roundAvatar, + round } } }) + ``` ::: ### API -d-skeleton 参数 -| 参数 | 类型 | 默认 | 说明 | +d-skeleton +| 参数 | 类型 | 默认 | 说明 | | :-----: | :-------: | :-----: | :-------------------------------------------- | -| row | `number` | `0` | 段落占位图行数 | | loading | `boolean` | `true` | 是否显示骨架屏,传 `false` 时会展示子组件内容 | -| animate | `boolean` | `true` | 是否开启动画 | -| avatar | `boolean` | `false` | 是否显示头像占位图 | +| animate | `boolean` | `true` | 是否开启动画 | +| avatar | `boolean` | `false` | 是否显示头像占位图 | +| title | `boolean` | `true` | 是否显示标题占位图 | +| paragraph | `boolean` | `true` | 是否显示段落占位图 | +| round | `boolean` | `false` | 是否将标题和段落显示为圆角风格 | + +d-skeleton-avatar-props +| 参数 | 类型 | 默认 | 说明 | +| :-----: | :-------: | :-----: | :-------------------------------------------- | +| avatar-size | `number \| string` | `40px` | 头像占位图大小 | +| avatar-shape | `string` | `round` | 头像占位图形状,可选值为`square` | + + +d-skeleton-title-props +| 参数 | 类型 | 默认 | 说明 | +| :-----: | :-------: | :-----: | :-------------------------------------------- | +| title-width | `number \| string` | `40%` | 设置标题占位图的宽度 | + + +d-skeleton-paragraph-props +| 参数 | 类型 | 默认 | 说明 | +| :-----: | :-------: | :-----: | :-------------------------------------------- | +| row | `number` | `0` | 段落占位图行数 | +| row-width | `number \| string \| (number \| string)[]` | `"1"` | 段落占位图宽度,可传数组来设置每一行的宽度 | +