refactor: 整理组件目录结构
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 4m47s
Docker Build & Deploy / Deploy to Production (push) Has been skipped
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 2s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s

- TransactionDetail, CategoryBillPopup 移入 Transaction/
- BudgetTypeTabs 移入 Budget/
- GlassBottomNav, ModernEmpty 移入 Global/
- Icon, IconSelector, ClassifySelector 等 8 个通用组件移入 Common/
- 更新所有相关引用路径
This commit is contained in:
SunCheng
2026-02-21 10:10:16 +08:00
parent b173c83134
commit 045158730f
38 changed files with 40 additions and 40 deletions

View File

@@ -0,0 +1,184 @@
<template>
<header class="calendar-header">
<!-- 左箭头 -->
<button
class="nav-btn"
aria-label="上一个周期"
@click="emit('prev')"
>
<van-icon name="arrow-left" />
</button>
<!-- 标题内容可点击跳转 -->
<div
class="header-content"
@click="emit('jump')"
>
<h1 class="header-title">
{{ formattedTitle }}
</h1>
</div>
<!-- 右箭头 -->
<button
class="nav-btn"
aria-label="下一个周期"
@click="emit('next')"
>
<van-icon name="arrow" />
</button>
<!-- 通知按钮 -->
<button
v-if="showNotification"
class="notif-btn"
aria-label="通知"
@click="emit('notification')"
>
<van-icon name="bell" />
</button>
</header>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
type: {
type: String,
required: true,
validator: (value) => ['week', 'month', 'year'].includes(value)
},
currentDate: {
type: Date,
required: true
},
showNotification: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['prev', 'next', 'jump', 'notification'])
/**
* 计算 ISO 8601 标准的周数
* @param date 目标日期
* @returns 周数 (1-53)
*/
const getISOWeek = (date) => {
const target = new Date(date.valueOf())
const dayNr = (date.getDay() + 6) % 7 // 周一为0周日为6
target.setDate(target.getDate() - dayNr + 3) // 本周四
const firstThursday = new Date(target.getFullYear(), 0, 4) // 该年第一个周四
const weekDiff = Math.floor((target.valueOf() - firstThursday.valueOf()) / 86400000)
return 1 + Math.floor(weekDiff / 7)
}
/**
* 计算 ISO 8601 标准的年份(用于周数)
* 注意:年末/年初的周可能属于相邻年份
*/
const getISOYear = (date) => {
const target = new Date(date.valueOf())
const dayNr = (date.getDay() + 6) % 7
target.setDate(target.getDate() - dayNr + 3) // 本周四
return target.getFullYear()
}
// 格式化标题
const formattedTitle = computed(() => {
const date = props.currentDate
const year = date.getFullYear()
const month = date.getMonth() + 1
switch (props.type) {
case 'week': {
const isoYear = getISOYear(date)
const weekNum = getISOWeek(date)
return `${isoYear}年第${weekNum}`
}
case 'month':
return `${year}${month}`
case 'year':
return `${year}`
default:
return ''
}
})
</script>
<style scoped lang="scss">
@import '@/assets/theme.css';
/* ========== 头部 ========== */
.calendar-header {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 8px 24px;
gap: 8px;
background: transparent !important;
position: relative;
z-index: 1;
min-height: 60px; /* 与 balance-header 保持一致,防止切换抖动 */
}
.header-content {
display: flex;
flex-direction: column;
gap: 4px;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.header-title {
font-family: var(--font-primary);
font-size: var(--font-2xl);
font-weight: var(--font-medium);
color: var(--text-primary);
margin: 0;
transition: opacity 0.2s;
}
.header-content:active .header-title {
opacity: 0.6;
}
.notif-btn {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: var(--radius-full);
background-color: var(--bg-button);
border: none;
cursor: pointer;
transition: opacity 0.2s;
margin-left: auto;
}
.notif-btn:active {
opacity: 0.7;
}
.nav-btn {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 18px;
background-color: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
}
.nav-btn:active {
background-color: var(--bg-tertiary);
}
</style>