This commit is contained in:
xiaojibeier
2026-07-06 03:36:46 +08:00
parent 300b15aa9f
commit f3726f0810
506 changed files with 221985 additions and 1 deletions
+7
View File
@@ -0,0 +1,7 @@
import VabCard from './index.vue'
VabCard.install = function(app) {
app.component(VabCard.name, VabCard)
}
export default VabCard
+81
View File
@@ -0,0 +1,81 @@
<template>
<el-card :body-class="props.bodyClass" :body-style="props.bodyStyle" :class="['vab-card', $attrs.class]" :shadow="props.shadow">
<template v-if="$slots.header || props.title" #header>
<slot v-if="$slots.header" name="header"></slot>
<template v-else>{{ props.title }}</template>
</template>
<el-skeleton v-if="props.skeleton" animated :loading="skeletonShow" :rows="props.skeletonRows">
<template #default>
<slot></slot>
</template>
</el-skeleton>
<slot v-else></slot>
<template v-if="$slots.footer" #footer>
<slot name="footer"></slot>
</template>
</el-card>
</template>
<script setup>
import { ref, onBeforeUnmount } from 'vue'
import { ElCard } from 'element-plus'
// 定义组件名称
defineOptions({
name: 'VabCard',
})
// 定义 props
const props = defineProps({
...ElCard.props,
shadow: {
type: String,
default: 'never',
},
skeleton: {
type: Boolean,
default: false,
},
skeletonRows: {
type: Number,
default: 5, // 显示的数量会比传入的数量多 1
},
title: {
type: String,
default: '',
},
})
const skeletonShow = ref(true)
const timer = setTimeout(() => {
skeletonShow.value = false
}, 500)
onBeforeUnmount(() => {
if (timer) clearTimeout(timer)
})
</script>
<style lang="scss" scoped>
.vab-card {
:deep() {
.el-card__header {
font-weight: 500;
color: inherit;
background: inherit;
[class*='ri-'] {
background-image: linear-gradient(120deg, #bd34fe 30%, var(--el-color-primary));
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.el-skeleton {
height: 100%;
overflow: hidden;
}
}
}
</style>
+7
View File
@@ -0,0 +1,7 @@
import VabColorfulCard from './index.vue'
VabColorfulCard.install = function(app) {
app.component(VabColorfulCard.name, VabColorfulCard)
}
export default VabColorfulCard
+92
View File
@@ -0,0 +1,92 @@
<template>
<el-card
:body-style="props.bodyStyle"
class="vab-colorful-card"
:shadow="props.shadow"
:style="
props.style
? props.style
: {
background: `linear-gradient(135deg, ${props.colorFrom} 15%, ${props.colorTo} 85%)`,
}
"
>
<!-- 头部插槽 -->
<template v-if="$slots.header" #header>
<slot name="header"></slot>
</template>
<!-- 使用 Iconify -->
<Icon v-if="props.icon" :icon="props.icon" class="card-icon" />
<!-- 默认插槽 -->
<slot></slot>
</el-card>
</template>
<script setup>
import { ElCard } from 'element-plus'
import { Icon } from '@iconify/vue' // 导入 Iconify
// 定义组件名称
defineOptions({
name: 'VabColorfulCard',
})
// 定义 props
const props = defineProps({
bodyStyle: {
type: [String, Object],
default: ''
},
shadow: {
type: String,
default: 'always',
},
colorFrom: {
type: String,
default: '',
},
colorTo: {
type: String,
default: '',
},
title: {
type: String,
default: '',
},
icon: {
type: String,
default: '',
},
style: {
type: Object,
default: () => ({}),
},
})
</script>
<style lang="scss" scoped>
.vab-colorful-card {
position: relative;
min-height: 120px;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.1); // 更淡的边框
border-radius: 4px; // 稍微小一点的圆角
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); // 更轻的阴影
transition: all 0.25s;
:deep(.el-card__header) {
color: var(--el-color-white);
}
:deep(.el-card__body) {
padding: 20px;
}
.card-icon {
position: absolute;
right: 20px;
font-size: 60px;
transform: rotate(15deg);
}
}
</style>
+7
View File
@@ -0,0 +1,7 @@
import VabCount from './index.vue'
VabCount.install = function(app) {
app.component(VabCount.name, VabCount)
}
export default VabCount
+106
View File
@@ -0,0 +1,106 @@
<template>
<span>{{ displayValue }}</span>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { isNumber } from '@/utils/validate'
import { TransitionPresets, useTransition } from '@vueuse/core'
defineOptions({
name: 'VabCount',
})
const props = defineProps({
startValue: {
type: Number,
required: false,
default: 0,
},
endValue: {
type: Number,
required: false,
default: 20,
},
duration: {
type: Number,
required: false,
default: 3000,
},
autoplay: {
type: Boolean,
required: false,
default: true,
},
decimals: {
type: Number,
required: false,
default: 0,
validator(value) {
return value >= 0
},
},
decimal: {
type: String,
required: false,
default: '.',
},
separator: {
type: String,
required: false,
default: ',',
},
prefix: {
type: String,
required: false,
default: '',
},
suffix: {
type: String,
required: false,
default: '',
},
useEasing: {
type: Boolean,
required: false,
default: true,
},
easingFn: {
type: Array,
default: () => [0.2, 0.2, 0, 1],
},
})
const source = ref(props.startValue)
const output = useTransition(source, {
duration: props.duration,
transition: props.useEasing ? props.easingFn : TransitionPresets.linear,
})
const formatNumber = (num) => {
num = num.toFixed(props.decimals)
num += ''
const x = num.split('.')
let x1 = x[0]
const x2 = x.length > 1 ? props.decimal + x[1] : ''
const rgx = /(\d+)(\d{3})/
if (props.separator && !isNumber(props.separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, `$1${props.separator}$2`)
}
}
return props.prefix + x1 + x2 + props.suffix
}
const displayValue = computed(() => formatNumber(output.value))
watch(
props,
(props) => {
if (props.autoplay) {
source.value = props.endValue
}
},
{ immediate: true }
)
</script>