Files
EmailBill/Web/src/components/Common/DateSelectHeader.vue
SunCheng 045158730f
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
refactor: 整理组件目录结构
- TransactionDetail, CategoryBillPopup 移入 Transaction/
- BudgetTypeTabs 移入 Budget/
- GlassBottomNav, ModernEmpty 移入 Global/
- Icon, IconSelector, ClassifySelector 等 8 个通用组件移入 Common/
- 更新所有相关引用路径
2026-02-21 10:10:16 +08:00

185 lines
3.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>