1
This commit is contained in:
183
Web/src/components/DateSelectHeader.vue
Normal file
183
Web/src/components/DateSelectHeader.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<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;
|
||||
}
|
||||
|
||||
.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>
|
||||
Reference in New Issue
Block a user