220 lines
4.1 KiB
Vue
220 lines
4.1 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="common-card budget-card" @click="$emit('click')">
|
|||
|
|
<div class="card-header">
|
|||
|
|
<div class="budget-info">
|
|||
|
|
<h3 class="card-title">{{ budget.name }}</h3>
|
|||
|
|
<slot name="tag">
|
|||
|
|
<van-tag v-if="budget.isStopped" type="danger" size="small" plain>已停止</van-tag>
|
|||
|
|
<van-tag v-else :type="statusTagType" size="small" plain>{{ statusTagText }}</van-tag>
|
|||
|
|
</slot>
|
|||
|
|
</div>
|
|||
|
|
<div class="header-actions">
|
|||
|
|
<slot name="actions">
|
|||
|
|
<van-button
|
|||
|
|
:icon="budget.isStopped ? 'play' : 'pause'"
|
|||
|
|
size="mini"
|
|||
|
|
plain
|
|||
|
|
round
|
|||
|
|
@click.stop="$emit('toggle-stop', budget)"
|
|||
|
|
/>
|
|||
|
|
</slot>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="budget-body">
|
|||
|
|
<div class="amount-info">
|
|||
|
|
<slot name="amount-info"></slot>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="progress-section">
|
|||
|
|
<div class="progress-info">
|
|||
|
|
<slot name="progress-info">
|
|||
|
|
<span class="period-type">{{ periodLabel }}进度</span>
|
|||
|
|
<span class="percent" :class="percentClass">
|
|||
|
|
{{ percentage }}%
|
|||
|
|
</span>
|
|||
|
|
</slot>
|
|||
|
|
</div>
|
|||
|
|
<van-progress
|
|||
|
|
:percentage="Math.min(percentage, 100)"
|
|||
|
|
stroke-width="8"
|
|||
|
|
:color="progressColor"
|
|||
|
|
:show-pivot="false"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card-footer">
|
|||
|
|
<div class="period-navigation" @click.stop>
|
|||
|
|
<van-button
|
|||
|
|
icon="arrow-left"
|
|||
|
|
class="nav-icon"
|
|||
|
|
plain
|
|||
|
|
size="small"
|
|||
|
|
style="width: 50px;"
|
|||
|
|
@click="$emit('switch-period', -1)"
|
|||
|
|
/>
|
|||
|
|
<span class="period-text">{{ budget.period }}</span>
|
|||
|
|
<van-button
|
|||
|
|
icon="arrow"
|
|||
|
|
class="nav-icon"
|
|||
|
|
plain
|
|||
|
|
size="small"
|
|||
|
|
style="width: 50px;"
|
|||
|
|
@click="$emit('switch-period', 1)"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { computed } from 'vue'
|
|||
|
|
|
|||
|
|
const props = defineProps({
|
|||
|
|
budget: {
|
|||
|
|
type: Object,
|
|||
|
|
required: true
|
|||
|
|
},
|
|||
|
|
statusTagType: {
|
|||
|
|
type: String,
|
|||
|
|
default: 'success'
|
|||
|
|
},
|
|||
|
|
statusTagText: {
|
|||
|
|
type: String,
|
|||
|
|
default: '进行中'
|
|||
|
|
},
|
|||
|
|
progressColor: {
|
|||
|
|
type: String,
|
|||
|
|
default: '#1989fa'
|
|||
|
|
},
|
|||
|
|
percentClass: {
|
|||
|
|
type: [String, Object],
|
|||
|
|
default: ''
|
|||
|
|
},
|
|||
|
|
periodLabel: {
|
|||
|
|
type: String,
|
|||
|
|
default: ''
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
defineEmits(['toggle-stop', 'switch-period', 'click'])
|
|||
|
|
|
|||
|
|
const percentage = computed(() => {
|
|||
|
|
if (!props.budget.limit) return 0
|
|||
|
|
return Math.round((props.budget.current / props.budget.limit) * 100)
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.budget-card {
|
|||
|
|
margin-left: 0;
|
|||
|
|
margin-right: 0;
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
padding-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.budget-info {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.card-title {
|
|||
|
|
margin: 0;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-actions {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.amount-info {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin: 16px 0;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.info-item) .label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #969799;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.info-item) .value {
|
|||
|
|
font-size: 15px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.value.expense) {
|
|||
|
|
color: #ee0a24;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.value.income) {
|
|||
|
|
color: #07c160;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-section {
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-info {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
font-size: 13px;
|
|||
|
|
margin-bottom: 6px;
|
|||
|
|
color: #646566;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.percent.warning {
|
|||
|
|
color: #ff976a;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.percent.income {
|
|||
|
|
color: #07c160;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.card-footer {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
color: #969799;
|
|||
|
|
padding: 12px 12px 0;
|
|||
|
|
padding-top: 8px;
|
|||
|
|
border-top: 1px solid #ebedf0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.period-navigation {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.period-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #323233;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-icon {
|
|||
|
|
padding: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #1989fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (prefers-color-scheme: dark) {
|
|||
|
|
.card-footer {
|
|||
|
|
border-top-color: #2c2c2c;
|
|||
|
|
}
|
|||
|
|
.period-text {
|
|||
|
|
color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|