首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<view class="skeleton-screen" :style="screenStyle">
|
||||
<block v-if="type === 'list'">
|
||||
<view
|
||||
v-for="n in count"
|
||||
:key="'list-' + n"
|
||||
class="skeleton-list-item"
|
||||
:style="cardStyle"
|
||||
>
|
||||
<view class="skeleton-avatar" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-content">
|
||||
<view class="skeleton-title" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-subtitle" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-subtitle skeleton-subtitle--short" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-tags">
|
||||
<view class="skeleton-tag" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-tag" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-else-if="type === 'card'">
|
||||
<view class="skeleton-card" :style="cardStyle">
|
||||
<view class="skeleton-card-header">
|
||||
<view class="skeleton-title skeleton-title--lg" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-subtitle" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-card-body">
|
||||
<view class="skeleton-line" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-line" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-line skeleton-line--short" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-card-footer">
|
||||
<view class="skeleton-line skeleton-line--footer" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-line skeleton-line--footer" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-else-if="type === 'stats'">
|
||||
<view
|
||||
v-for="n in count"
|
||||
:key="'stats-' + n"
|
||||
class="skeleton-stats"
|
||||
:style="cardStyle"
|
||||
>
|
||||
<view class="skeleton-stat-col">
|
||||
<view class="skeleton-stat-value" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-stat-label" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-stat-col">
|
||||
<view class="skeleton-stat-value" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-stat-label" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-stat-col">
|
||||
<view class="skeleton-stat-value" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-stat-label" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-else-if="type === 'dashboard'">
|
||||
<view class="skeleton-dashboard" :style="cardStyle">
|
||||
<view class="skeleton-ring-wrapper">
|
||||
<view class="skeleton-ring" :style="ringStyle"></view>
|
||||
<view class="skeleton-ring-inner" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-dashboard-stats">
|
||||
<view class="skeleton-stat-col">
|
||||
<view class="skeleton-stat-value" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-stat-label" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-stat-col">
|
||||
<view class="skeleton-stat-value" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-stat-label" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-for="n in dashboardListCount"
|
||||
:key="'dash-list-' + n"
|
||||
class="skeleton-list-item skeleton-list-item--compact"
|
||||
:style="cardStyle"
|
||||
>
|
||||
<view class="skeleton-dot" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-content">
|
||||
<view class="skeleton-title" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-subtitle" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-else-if="type === 'form'">
|
||||
<view class="skeleton-form" :style="cardStyle">
|
||||
<view
|
||||
v-for="n in count"
|
||||
:key="'form-' + n"
|
||||
class="skeleton-form-field"
|
||||
>
|
||||
<view class="skeleton-label" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-input" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
<view class="skeleton-form-actions">
|
||||
<view class="skeleton-btn skeleton-btn--primary" :style="shimmerStyle"></view>
|
||||
<view class="skeleton-btn skeleton-btn--secondary" :style="shimmerStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block v-else-if="type === 'custom'">
|
||||
<view class="skeleton-custom" :style="cardStyle">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import themeMixin from '@/mixins/themeMixin.js'
|
||||
import { THEME_CHANGE_EVENT } from '@/utils/theme'
|
||||
|
||||
export default {
|
||||
name: 'SkeletonScreen',
|
||||
mixins: [themeMixin],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'list',
|
||||
validator(val) {
|
||||
return ['list', 'card', 'stats', 'dashboard', 'form', 'custom'].indexOf(val) !== -1
|
||||
}
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
animated: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
screenStyle() {
|
||||
return {
|
||||
backgroundColor: this.themeConfig.bgColor
|
||||
}
|
||||
},
|
||||
cardStyle() {
|
||||
return {
|
||||
backgroundColor: this.themeConfig.cardBgColor,
|
||||
borderRadius: '20rpx',
|
||||
padding: '24rpx',
|
||||
marginBottom: '20rpx'
|
||||
}
|
||||
},
|
||||
shimmerStyle() {
|
||||
if (!this.animated) {
|
||||
return {
|
||||
backgroundColor: this.shimmerBaseColor
|
||||
}
|
||||
}
|
||||
return {
|
||||
background: 'linear-gradient(90deg, ' +
|
||||
this.shimmerBaseColor + ' 25%, ' +
|
||||
this.shimmerHighlightColor + ' 50%, ' +
|
||||
this.shimmerBaseColor + ' 75%)',
|
||||
backgroundSize: '200% 100%',
|
||||
animation: 'skeleton-shimmer 1.5s infinite'
|
||||
}
|
||||
},
|
||||
ringStyle() {
|
||||
if (!this.animated) {
|
||||
return {
|
||||
border: '12rpx solid ' + this.shimmerBaseColor
|
||||
}
|
||||
}
|
||||
return {
|
||||
border: '12rpx solid transparent',
|
||||
borderTopColor: this.shimmerBaseColor,
|
||||
animation: 'skeleton-shimmer-ring 1.2s infinite linear'
|
||||
}
|
||||
},
|
||||
shimmerBaseColor() {
|
||||
return this.themeConfig.borderLight || '#f0f0f0'
|
||||
},
|
||||
shimmerHighlightColor() {
|
||||
return this.lightenColor(this.shimmerBaseColor, 15)
|
||||
},
|
||||
dashboardListCount() {
|
||||
return Math.min(this.count, 3)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.syncThemeConfig()
|
||||
this.watchThemeChange()
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.themeChangeHandler) {
|
||||
uni.$off(THEME_CHANGE_EVENT, this.themeChangeHandler)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
lightenColor(hex, percent) {
|
||||
if (!hex || hex.charAt(0) !== '#') return hex
|
||||
const num = parseInt(hex.slice(1), 16)
|
||||
const r = Math.min(255, ((num >> 16) & 0xff) + Math.round(255 * percent / 100))
|
||||
const g = Math.min(255, ((num >> 8) & 0xff) + Math.round(255 * percent / 100))
|
||||
const b = Math.min(255, (num & 0xff) + Math.round(255 * percent / 100))
|
||||
return 'rgb(' + r + ', ' + g + ', ' + b + ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.skeleton-screen {
|
||||
min-height: 100vh;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.skeleton-list-item {
|
||||
display: flex;
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 20rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.skeleton-title {
|
||||
height: 36rpx;
|
||||
width: 60%;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-title--lg {
|
||||
height: 44rpx;
|
||||
width: 80%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-subtitle {
|
||||
height: 28rpx;
|
||||
width: 50%;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.skeleton-subtitle--short {
|
||||
width: 35%;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-tags {
|
||||
display: flex;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-tag {
|
||||
height: 40rpx;
|
||||
width: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
padding: 28rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-card-header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-card-body {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-line {
|
||||
height: 28rpx;
|
||||
width: 100%;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-line--short {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.skeleton-card-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.skeleton-line--footer {
|
||||
width: 30%;
|
||||
margin-right: 24rpx;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.skeleton-stats {
|
||||
display: flex;
|
||||
padding: 32rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-stat-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.skeleton-stat-value {
|
||||
height: 48rpx;
|
||||
width: 80%;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-stat-label {
|
||||
height: 24rpx;
|
||||
width: 60%;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.skeleton-dashboard {
|
||||
padding: 32rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-ring-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 24rpx 0 32rpx;
|
||||
position: relative;
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.skeleton-ring {
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.skeleton-ring-inner {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.skeleton-dashboard-stats {
|
||||
display: flex;
|
||||
padding-top: 24rpx;
|
||||
border-top: 2rpx solid;
|
||||
}
|
||||
|
||||
.skeleton-list-item--compact {
|
||||
padding: 20rpx 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.skeleton-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-form {
|
||||
padding: 28rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.skeleton-form-field {
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.skeleton-label {
|
||||
height: 24rpx;
|
||||
width: 30%;
|
||||
border-radius: 6rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.skeleton-input {
|
||||
height: 80rpx;
|
||||
width: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.skeleton-form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-btn {
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.skeleton-btn--primary {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-btn--secondary {
|
||||
flex: 0.6;
|
||||
}
|
||||
|
||||
.skeleton-custom {
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
@keyframes skeleton-shimmer {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes skeleton-shimmer-ring {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user