|
@@ -0,0 +1,753 @@
|
|
|
+<!--
|
|
|
+时间刻度尺组件 (TimeScale)
|
|
|
+
|
|
|
+功能:
|
|
|
+- 显示水平时间轴,包含月份标记和小刻度
|
|
|
+- 支持用户滑动选择时间
|
|
|
+- 每两天显示一个小刻度
|
|
|
+- 中间固定竖线,显示当前选中的时间
|
|
|
+- 时间格式:YYYY-MM-DD
|
|
|
+
|
|
|
+Props:
|
|
|
+- currentDate: String - 当前选中的时间 (YYYY-MM-DD格式)
|
|
|
+
|
|
|
+Events:
|
|
|
+- timeChange: (date: String) - 时间变化时触发,返回YYYY-MM-DD格式的日期
|
|
|
+
|
|
|
+使用示例:
|
|
|
+<time-scale
|
|
|
+ :current-date="selectedDate"
|
|
|
+ @time-change="handleTimeChange"
|
|
|
+></time-scale>
|
|
|
+-->
|
|
|
+
|
|
|
+<template>
|
|
|
+ <view class="time-scale-container">
|
|
|
+ <view class="time-scale-scroll">
|
|
|
+ <scroll-view
|
|
|
+ class="time-scale-wrapper"
|
|
|
+ scroll-x
|
|
|
+ :scroll-left="scrollLeft"
|
|
|
+ scroll-with-animation
|
|
|
+ @scroll="handleScroll"
|
|
|
+ @scrolltolower="handleScrollToLower"
|
|
|
+ @scrolltoupper="handleScrollToUpper"
|
|
|
+ ref="scrollViewRef"
|
|
|
+ >
|
|
|
+ <view class="time-scale">
|
|
|
+ <!-- 月份标签 -->
|
|
|
+ <view class="month-labels">
|
|
|
+ <view
|
|
|
+ class="month-label"
|
|
|
+ v-for="(month, index) in monthLabels"
|
|
|
+ :key="`month-${index}`"
|
|
|
+ :style="{ left: month.left + 'rpx' }"
|
|
|
+ >
|
|
|
+ {{ month.label }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 时间轴 -->
|
|
|
+ <view class="timeline">
|
|
|
+ <!-- 月份大点 -->
|
|
|
+ <view
|
|
|
+ class="month-dot"
|
|
|
+ v-for="(month, index) in monthLabels"
|
|
|
+ :key="`dot-${index}`"
|
|
|
+ :style="{ left: month.left + 'rpx' }"
|
|
|
+ ></view>
|
|
|
+
|
|
|
+ <!-- 小刻度 -->
|
|
|
+ <view
|
|
|
+ class="tick-mark"
|
|
|
+ v-for="(tick, index) in tickMarks"
|
|
|
+ :key="`tick-${index}`"
|
|
|
+ :style="{ left: tick.left + 'rpx' }"
|
|
|
+ ></view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <!-- 中间竖线 -->
|
|
|
+ <view class="center-line"></view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 当前时间显示 -->
|
|
|
+ <view class="current-time" v-if="currentTime">
|
|
|
+ {{ formatDisplayTime(currentTime) }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ // 当前选中的时间
|
|
|
+ currentDate: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const emit = defineEmits(['timeChange'])
|
|
|
+
|
|
|
+// 响应式数据
|
|
|
+const currentTime = ref('')
|
|
|
+let scrollTimer = null // 防抖定时器
|
|
|
+const scrollViewRef = ref(null) // 滚动视图引用
|
|
|
+const scrollLeft = ref(0) // 滚动位置
|
|
|
+
|
|
|
+// 计算属性:生成月份标签
|
|
|
+const monthLabels = computed(() => {
|
|
|
+ const labels = []
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth()
|
|
|
+
|
|
|
+ // 从当前月份往前一年的月份,从右到左排列
|
|
|
+ for (let i = 0; i <= 11; i++) {
|
|
|
+ const date = new Date(currentYear, currentMonth - i, 1)
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = date.getMonth() + 1
|
|
|
+
|
|
|
+ labels.push({
|
|
|
+ label: `${month}月`,
|
|
|
+ year: year,
|
|
|
+ month: month,
|
|
|
+ left: (11 - i) * 200 + 100, // 从右到左排列,当前月份在最右边
|
|
|
+ monthOffset: i // 添加月份偏移信息
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return labels
|
|
|
+})
|
|
|
+
|
|
|
+// 计算属性:生成小刻度
|
|
|
+const tickMarks = computed(() => {
|
|
|
+ const ticks = []
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth()
|
|
|
+
|
|
|
+ // 从当前月份往前一年的刻度,从右到左排列
|
|
|
+ for (let monthOffset = 0; monthOffset <= 11; monthOffset++) {
|
|
|
+ const date = new Date(currentYear, currentMonth - monthOffset, 1)
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = date.getMonth() + 1
|
|
|
+ const daysInMonth = new Date(year, month, 0).getDate()
|
|
|
+
|
|
|
+ // 每隔一天一个刻度,从第2天开始
|
|
|
+ for (let day = 2; day <= daysInMonth; day++) {
|
|
|
+ const monthLeft = (11 - monthOffset) * 200 + 100
|
|
|
+ const dayOffset = ((day - 1) / daysInMonth) * 200
|
|
|
+ ticks.push({
|
|
|
+ left: monthLeft + dayOffset,
|
|
|
+ date: `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ticks
|
|
|
+})
|
|
|
+
|
|
|
+// 格式化显示时间
|
|
|
+const formatDisplayTime = (dateStr) => {
|
|
|
+ const [year, month, day] = dateStr.split('-')
|
|
|
+ return `${year}年${month}月${day}日`
|
|
|
+}
|
|
|
+
|
|
|
+// 根据时间计算位置
|
|
|
+const calculatePositionByDate = (dateStr) => {
|
|
|
+ const [year, month, day] = dateStr.split('-').map(Number)
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth()
|
|
|
+
|
|
|
+ // 计算月份偏移(从当前月份往前)- 使用与月份标签相同的逻辑
|
|
|
+ // 月份标签: new Date(currentYear, currentMonth - i, 1) 其中 i 从 0 到 11
|
|
|
+ // 所以对于目标日期,我们需要找到对应的 i 值
|
|
|
+ let monthOffset = -1
|
|
|
+ for (let i = 0; i <= 11; i++) {
|
|
|
+ const testDate = new Date(currentYear, currentMonth - i, 1)
|
|
|
+ if (testDate.getFullYear() === year && testDate.getMonth() + 1 === month) {
|
|
|
+ monthOffset = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (monthOffset === -1 || monthOffset > 11) return 0
|
|
|
+
|
|
|
+ // 计算月份位置(从右到左)
|
|
|
+ const monthLeft = (11 - monthOffset) * 200 + 100
|
|
|
+
|
|
|
+ // 计算在月份内的位置,使用与calculateDateByPosition相同的计算逻辑
|
|
|
+ const daysInMonth = new Date(year, month, 0).getDate()
|
|
|
+ const dayRatio = (day - 1) / daysInMonth
|
|
|
+ const dayOffset = dayRatio * 200
|
|
|
+
|
|
|
+ return monthLeft + dayOffset
|
|
|
+}
|
|
|
+
|
|
|
+// 根据位置计算时间
|
|
|
+const calculateDateByPosition = (left) => {
|
|
|
+ // 计算月份索引(从右到左)
|
|
|
+ const monthIndex = Math.floor((left - 100) / 200)
|
|
|
+ const monthOffset = 11 - monthIndex
|
|
|
+
|
|
|
+ if (monthOffset < 0 || monthOffset > 11) return ''
|
|
|
+
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth()
|
|
|
+
|
|
|
+ // 计算目标月份 - 使用与月份标签完全相同的逻辑
|
|
|
+ // 月份标签: new Date(currentYear, currentMonth - i, 1) 其中 i 从 0 到 11
|
|
|
+ // 这里 monthOffset 对应月份标签中的 i
|
|
|
+ const targetDate = new Date(currentYear, currentMonth - monthOffset, 1)
|
|
|
+ const year = targetDate.getFullYear()
|
|
|
+ const month = targetDate.getMonth() + 1
|
|
|
+ const daysInMonth = new Date(year, month, 0).getDate()
|
|
|
+
|
|
|
+ // 计算在月份内的位置
|
|
|
+ const monthLeft = monthIndex * 200 + 100
|
|
|
+ const dayOffset = left - monthLeft
|
|
|
+
|
|
|
+ // 确保dayOffset在有效范围内
|
|
|
+ if (dayOffset < 0 || dayOffset > 200) return ''
|
|
|
+
|
|
|
+ // 计算日期比例(0-1),使用更精确的计算方法
|
|
|
+ const dayRatio = dayOffset / 200
|
|
|
+
|
|
|
+ // 计算日期(1-31),使用四舍五入而不是向下取整
|
|
|
+ let day = Math.round(dayRatio * daysInMonth) + 1
|
|
|
+
|
|
|
+ // 确保日期在有效范围内
|
|
|
+ day = Math.max(1, Math.min(daysInMonth, day))
|
|
|
+
|
|
|
+ return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
|
|
|
+}
|
|
|
+
|
|
|
+// 调试函数:验证时间计算
|
|
|
+const debugTimeCalculation = (left) => {
|
|
|
+ const date = calculateDateByPosition(left)
|
|
|
+ const position = calculatePositionByDate(date)
|
|
|
+ const difference = Math.abs(left - position)
|
|
|
+ console.log(`位置: ${left}, 计算时间: ${date}, 反向计算位置: ${position}, 误差: ${difference}`)
|
|
|
+ return date
|
|
|
+}
|
|
|
+
|
|
|
+// 精确调试函数:测试特定位置
|
|
|
+const preciseDebug = (left) => {
|
|
|
+ const date = calculateDateByPosition(left)
|
|
|
+ const [year, month, day] = date.split('-').map(Number)
|
|
|
+ const monthIndex = Math.floor((left - 100) / 200)
|
|
|
+ const monthOffset = 11 - monthIndex
|
|
|
+ const monthLeft = monthIndex * 200 + 100
|
|
|
+ const dayOffset = left - monthLeft
|
|
|
+ const dayRatio = dayOffset / 200
|
|
|
+ const daysInMonth = new Date(year, month, 0).getDate()
|
|
|
+
|
|
|
+ console.log(`=== 精确调试 ===`)
|
|
|
+ console.log(`输入位置: ${left}`)
|
|
|
+ console.log(`月份索引: ${monthIndex}, 月份偏移: ${monthOffset}`)
|
|
|
+ console.log(`月份左边界: ${monthLeft}, 日期偏移: ${dayOffset}`)
|
|
|
+ console.log(`日期比例: ${dayRatio}, 月份天数: ${daysInMonth}`)
|
|
|
+ console.log(`计算日期: ${day}, 最终时间: ${date}`)
|
|
|
+
|
|
|
+ // 添加反向验证
|
|
|
+ const reversePosition = calculatePositionByDate(date)
|
|
|
+ const reverseDate = calculateDateByPosition(reversePosition)
|
|
|
+ console.log(`反向验证: 位置 ${reversePosition} -> 时间 ${reverseDate}`)
|
|
|
+ console.log(`一致性检查: ${date === reverseDate ? '✅' : '❌'}`)
|
|
|
+
|
|
|
+ return date
|
|
|
+}
|
|
|
+
|
|
|
+// 时间差分析函数
|
|
|
+const analyzeTimeDifference = (left) => {
|
|
|
+ console.log(`=== 时间差分析 ===`)
|
|
|
+ console.log(`分析位置: ${left}`)
|
|
|
+
|
|
|
+ // 计算当前时间
|
|
|
+ const date = calculateDateByPosition(left)
|
|
|
+ console.log(`计算时间: ${date}`)
|
|
|
+
|
|
|
+ // 计算反向位置
|
|
|
+ const reversePosition = calculatePositionByDate(date)
|
|
|
+ console.log(`反向位置: ${reversePosition}`)
|
|
|
+
|
|
|
+ // 计算位置差
|
|
|
+ const positionDiff = Math.abs(left - reversePosition)
|
|
|
+ console.log(`位置差: ${positionDiff}`)
|
|
|
+
|
|
|
+ // 分析可能的原因
|
|
|
+ if (positionDiff > 5) {
|
|
|
+ console.log(`⚠️ 位置差较大,可能原因:`)
|
|
|
+ console.log(`1. 日期计算精度问题`)
|
|
|
+ console.log(`2. 月份边界处理问题`)
|
|
|
+ console.log(`3. 四舍五入误差`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return { date, reversePosition, positionDiff }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理滚动事件
|
|
|
+const handleScroll = (e) => {
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (scrollTimer) {
|
|
|
+ clearTimeout(scrollTimer)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置新的定时器,防抖处理
|
|
|
+ scrollTimer = setTimeout(() => {
|
|
|
+ const scrollLeft = e.detail.scrollLeft
|
|
|
+ const containerWidth = 750 // 假设容器宽度为750rpx
|
|
|
+ const centerPosition = containerWidth / 2
|
|
|
+
|
|
|
+ // 计算当前中心位置对应的时间
|
|
|
+ const absoluteLeft = scrollLeft + centerPosition
|
|
|
+ const date = calculateDateByPosition(absoluteLeft)
|
|
|
+
|
|
|
+ if (date && date !== currentTime.value) {
|
|
|
+ currentTime.value = date
|
|
|
+ emit('timeChange', date)
|
|
|
+ console.log('当前选中时间:', date) // 打印格式为YYYY-MM-DD
|
|
|
+
|
|
|
+ // 详细调试信息
|
|
|
+ console.log(`滚动位置: ${scrollLeft}, 中心位置: ${centerPosition}, 绝对位置: ${absoluteLeft}`)
|
|
|
+ preciseDebug(absoluteLeft)
|
|
|
+
|
|
|
+ // 时间差分析
|
|
|
+ analyzeTimeDifference(absoluteLeft)
|
|
|
+ }
|
|
|
+ }, 50) // 减少防抖时间,提高响应性
|
|
|
+}
|
|
|
+
|
|
|
+// 处理滚动到底部
|
|
|
+const handleScrollToLower = () => {
|
|
|
+ console.log('滚动到底部')
|
|
|
+}
|
|
|
+
|
|
|
+// 处理滚动到顶部
|
|
|
+const handleScrollToUpper = () => {
|
|
|
+ console.log('滚动到顶部')
|
|
|
+}
|
|
|
+
|
|
|
+// 监听props变化
|
|
|
+watch(() => props.currentDate, (newDate) => {
|
|
|
+ if (newDate && newDate !== currentTime.value) {
|
|
|
+ currentTime.value = newDate
|
|
|
+ }
|
|
|
+}, { immediate: true })
|
|
|
+
|
|
|
+// 验证月份标签和位置计算的对应关系
|
|
|
+const validateMonthPositionMapping = () => {
|
|
|
+ console.log('=== 月份位置映射验证 ===')
|
|
|
+
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ const expectedDate = `${label.year}-${label.month.toString().padStart(2, '0')}-01`
|
|
|
+ const calculatedPosition = calculatePositionByDate(expectedDate)
|
|
|
+ const reverseDate = calculateDateByPosition(calculatedPosition)
|
|
|
+
|
|
|
+ console.log(`月份${index + 1}: ${label.label}`)
|
|
|
+ console.log(` 标签位置: ${label.left}`)
|
|
|
+ console.log(` 期望日期: ${expectedDate}`)
|
|
|
+ console.log(` 计算位置: ${calculatedPosition}`)
|
|
|
+ console.log(` 反向日期: ${reverseDate}`)
|
|
|
+ console.log(` 位置一致: ${Math.abs(label.left - calculatedPosition) < 1 ? '✅' : '❌'}`)
|
|
|
+ console.log(` 日期一致: ${expectedDate === reverseDate ? '✅' : '❌'}`)
|
|
|
+ console.log('---')
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 验证月份标签和位置计算的一致性
|
|
|
+const validateMonthConsistency = () => {
|
|
|
+ console.log('=== 月份一致性验证 ===')
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ const expectedDate = `${label.year}-${label.month.toString().padStart(2, '0')}-01`
|
|
|
+ const calculatedPosition = calculatePositionByDate(expectedDate)
|
|
|
+ const reverseDate = calculateDateByPosition(calculatedPosition)
|
|
|
+ const difference = Math.abs(label.left - calculatedPosition)
|
|
|
+
|
|
|
+ console.log(`月份${index + 1}: ${label.label} (${expectedDate})`)
|
|
|
+ console.log(` 标签位置: ${label.left}, 计算位置: ${calculatedPosition}, 误差: ${difference}`)
|
|
|
+ console.log(` 反向验证: ${reverseDate} vs ${expectedDate}`)
|
|
|
+ console.log(` 一致性: ${reverseDate === expectedDate ? '✅' : '❌'}`)
|
|
|
+ console.log('---')
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 验证月份标签位置
|
|
|
+const validateMonthLabels = () => {
|
|
|
+ console.log('=== 月份标签验证 ===')
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ const expectedDate = `${label.year}-${label.month.toString().padStart(2, '0')}-01`
|
|
|
+ const calculatedPosition = calculatePositionByDate(expectedDate)
|
|
|
+ const difference = Math.abs(label.left - calculatedPosition)
|
|
|
+ console.log(`月份${index + 1}: ${label.label} (${expectedDate}) -> 标签位置: ${label.left}, 计算位置: ${calculatedPosition}, 误差: ${difference}`)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 测试函数:验证时间计算逻辑
|
|
|
+const testTimeCalculation = () => {
|
|
|
+ console.log('=== 时间计算测试 ===')
|
|
|
+
|
|
|
+ // 先验证月份位置映射
|
|
|
+ validateMonthPositionMapping()
|
|
|
+
|
|
|
+ // 再验证月份一致性
|
|
|
+ validateMonthConsistency()
|
|
|
+
|
|
|
+ // 再验证月份标签
|
|
|
+ validateMonthLabels()
|
|
|
+
|
|
|
+ // 测试几个关键位置(从右到左)
|
|
|
+ const testPositions = [
|
|
|
+ 2300, // 最右边(当前月份第一天)
|
|
|
+ 2100, // 当前月份中间
|
|
|
+ 1900, // 上个月第一天
|
|
|
+ 1700, // 上上个月第一天
|
|
|
+ 1500, // 三个月前第一天
|
|
|
+ 1300, // 四个月前第一天
|
|
|
+ 1100, // 五个月前第一天
|
|
|
+ 900, // 六个月前第一天
|
|
|
+ 700, // 七个月前第一天
|
|
|
+ 500, // 八个月前第一天
|
|
|
+ 300, // 九个月前第一天
|
|
|
+ 100 // 最左边(一年前)
|
|
|
+ ]
|
|
|
+
|
|
|
+ testPositions.forEach((pos, index) => {
|
|
|
+ const date = calculateDateByPosition(pos)
|
|
|
+ const reversePos = calculatePositionByDate(date)
|
|
|
+ const difference = Math.abs(pos - reversePos)
|
|
|
+ console.log(`测试${index + 1}: 位置 ${pos} -> 时间 ${date} -> 反向位置 ${reversePos}, 误差: ${difference}`)
|
|
|
+
|
|
|
+ // 如果误差太大,输出详细调试信息
|
|
|
+ if (difference > 10) {
|
|
|
+ console.log('--- 误差过大,详细调试 ---')
|
|
|
+ preciseDebug(pos)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 显示月份对比信息
|
|
|
+const showMonthComparison = () => {
|
|
|
+ console.log('=== 月份对比信息 ===')
|
|
|
+
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth() + 1
|
|
|
+
|
|
|
+ console.log(`当前时间: ${currentYear}年${currentMonth}月`)
|
|
|
+ console.log(`月份标签数量: ${monthLabels.value.length}`)
|
|
|
+
|
|
|
+ // 显示所有月份标签
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ console.log(`标签${index + 1}: ${label.label} (${label.year}-${label.month}) 位置: ${label.left}`)
|
|
|
+
|
|
|
+ // 计算该月份第一天对应的位置
|
|
|
+ const firstDayDate = `${label.year}-${label.month.toString().padStart(2, '0')}-01`
|
|
|
+ const calculatedPosition = calculatePositionByDate(firstDayDate)
|
|
|
+ const reverseDate = calculateDateByPosition(calculatedPosition)
|
|
|
+
|
|
|
+ console.log(` 计算位置: ${calculatedPosition}, 反向日期: ${reverseDate}`)
|
|
|
+ console.log(` 位置一致: ${Math.abs(label.left - calculatedPosition) < 1 ? '✅' : '❌'}`)
|
|
|
+ console.log(` 日期一致: ${firstDayDate === reverseDate ? '✅' : '❌'}`)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 显示当前月份信息
|
|
|
+const showCurrentMonthInfo = () => {
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth() + 1
|
|
|
+
|
|
|
+ console.log('=== 当前月份信息 ===')
|
|
|
+ console.log(`当前年份: ${currentYear}, 当前月份: ${currentMonth}`)
|
|
|
+ console.log(`月份标签数量: ${monthLabels.value.length}`)
|
|
|
+
|
|
|
+ // 显示所有月份标签
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ console.log(`标签${index + 1}: ${label.label} (${label.year}-${label.month}) 位置: ${label.left}`)
|
|
|
+ })
|
|
|
+
|
|
|
+ return { year: currentYear, month: currentMonth }
|
|
|
+}
|
|
|
+
|
|
|
+// 显示当前日期信息
|
|
|
+const showCurrentDateInfo = () => {
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth() + 1
|
|
|
+ const currentDay = now.getDate()
|
|
|
+
|
|
|
+ console.log('=== 当前日期信息 ===')
|
|
|
+ console.log(`当前日期: ${currentYear}-${currentMonth.toString().padStart(2, '0')}-${currentDay.toString().padStart(2, '0')}`)
|
|
|
+ console.log(`当前年份: ${currentYear}, 当前月份: ${currentMonth}, 当前日期: ${currentDay}`)
|
|
|
+
|
|
|
+ // 计算当前日期在时间轴上的位置
|
|
|
+ const currentDateStr = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-${currentDay.toString().padStart(2, '0')}`
|
|
|
+ const currentPosition = calculatePositionByDate(currentDateStr)
|
|
|
+ console.log(`当前日期在时间轴上的位置: ${currentPosition}`)
|
|
|
+
|
|
|
+ return currentDateStr
|
|
|
+}
|
|
|
+
|
|
|
+// 测试每天刻度的准确性
|
|
|
+const testDailyTicks = () => {
|
|
|
+ console.log('=== 每天刻度测试 ===')
|
|
|
+
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth()
|
|
|
+
|
|
|
+ // 测试当前月份的几个关键日期
|
|
|
+ const testDays = [1, 5, 10, 15, 20, 25, 30]
|
|
|
+
|
|
|
+ testDays.forEach(day => {
|
|
|
+ const testDate = new Date(currentYear, currentMonth, day)
|
|
|
+ const year = testDate.getFullYear()
|
|
|
+ const month = testDate.getMonth() + 1
|
|
|
+ const daysInMonth = new Date(year, month, 0).getDate()
|
|
|
+
|
|
|
+ // 只测试有效的日期
|
|
|
+ if (day <= daysInMonth) {
|
|
|
+ const dateStr = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
|
|
|
+ const position = calculatePositionByDate(dateStr)
|
|
|
+ const reverseDate = calculateDateByPosition(position)
|
|
|
+ const isAccurate = dateStr === reverseDate
|
|
|
+
|
|
|
+ console.log(`日期: ${dateStr} -> 位置: ${position} -> 反向: ${reverseDate} ${isAccurate ? '✅' : '❌'}`)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 简单测试:验证月份对应关系
|
|
|
+const simpleTest = () => {
|
|
|
+ console.log('=== 简单测试 ===')
|
|
|
+
|
|
|
+ // 测试当前月份
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth() + 1
|
|
|
+ const currentDate = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-01`
|
|
|
+
|
|
|
+ console.log(`当前月份: ${currentMonth}月`)
|
|
|
+ console.log(`当前日期: ${currentDate}`)
|
|
|
+
|
|
|
+ // 计算位置
|
|
|
+ const position = calculatePositionByDate(currentDate)
|
|
|
+ console.log(`计算位置: ${position}`)
|
|
|
+
|
|
|
+ // 反向计算时间
|
|
|
+ const reverseDate = calculateDateByPosition(position)
|
|
|
+ console.log(`反向计算: ${reverseDate}`)
|
|
|
+
|
|
|
+ // 验证一致性
|
|
|
+ const isConsistent = currentDate === reverseDate
|
|
|
+ console.log(`一致性: ${isConsistent ? '✅' : '❌'}`)
|
|
|
+
|
|
|
+ return isConsistent
|
|
|
+}
|
|
|
+
|
|
|
+// 测试3个月差问题
|
|
|
+const testThreeMonthDifference = () => {
|
|
|
+ console.log('=== 3个月差问题测试 ===')
|
|
|
+
|
|
|
+ const now = new Date()
|
|
|
+ const currentYear = now.getFullYear()
|
|
|
+ const currentMonth = now.getMonth() + 1
|
|
|
+
|
|
|
+ console.log(`当前时间: ${currentYear}年${currentMonth}月`)
|
|
|
+
|
|
|
+ // 测试当前月份的位置
|
|
|
+ const currentMonthDate = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-01`
|
|
|
+ const currentPosition = calculatePositionByDate(currentMonthDate)
|
|
|
+ const currentReverseDate = calculateDateByPosition(currentPosition)
|
|
|
+
|
|
|
+ console.log(`当前月份: ${currentMonthDate}`)
|
|
|
+ console.log(`计算位置: ${currentPosition}`)
|
|
|
+ console.log(`反向计算: ${currentReverseDate}`)
|
|
|
+ console.log(`当前月份一致性: ${currentMonthDate === currentReverseDate ? '✅' : '❌'}`)
|
|
|
+
|
|
|
+ // 测试3个月前的月份
|
|
|
+ const threeMonthsAgo = new Date(currentYear, currentMonth - 4, 1) // 减4是因为getMonth()返回0-11
|
|
|
+ const threeMonthsAgoYear = threeMonthsAgo.getFullYear()
|
|
|
+ const threeMonthsAgoMonth = threeMonthsAgo.getMonth() + 1
|
|
|
+ const threeMonthsAgoDate = `${threeMonthsAgoYear}-${threeMonthsAgoMonth.toString().padStart(2, '0')}-01`
|
|
|
+
|
|
|
+ const threeMonthsPosition = calculatePositionByDate(threeMonthsAgoDate)
|
|
|
+ const threeMonthsReverseDate = calculateDateByPosition(threeMonthsPosition)
|
|
|
+
|
|
|
+ console.log(`3个月前: ${threeMonthsAgoDate}`)
|
|
|
+ console.log(`计算位置: ${threeMonthsPosition}`)
|
|
|
+ console.log(`反向计算: ${threeMonthsReverseDate}`)
|
|
|
+ console.log(`3个月前一致性: ${threeMonthsAgoDate === threeMonthsReverseDate ? '✅' : '❌'}`)
|
|
|
+
|
|
|
+ // 检查月份标签
|
|
|
+ console.log('月份标签检查:')
|
|
|
+ monthLabels.value.forEach((label, index) => {
|
|
|
+ if (label.year === threeMonthsAgoYear && label.month === threeMonthsAgoMonth) {
|
|
|
+ console.log(`找到3个月前标签: ${label.label} 位置: ${label.left}`)
|
|
|
+ console.log(`位置差: ${Math.abs(label.left - threeMonthsPosition)}`)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 组件挂载后初始化
|
|
|
+onMounted(() => {
|
|
|
+ // 运行简单测试
|
|
|
+ simpleTest()
|
|
|
+
|
|
|
+ // 测试每天刻度
|
|
|
+ testDailyTicks()
|
|
|
+
|
|
|
+ // 测试3个月差问题
|
|
|
+ testThreeMonthDifference()
|
|
|
+
|
|
|
+ // 显示月份对比信息
|
|
|
+ showMonthComparison()
|
|
|
+
|
|
|
+ // 显示当前月份信息
|
|
|
+ showCurrentMonthInfo()
|
|
|
+
|
|
|
+ // 显示当前日期信息
|
|
|
+ showCurrentDateInfo()
|
|
|
+
|
|
|
+ // 运行测试
|
|
|
+ testTimeCalculation()
|
|
|
+
|
|
|
+ // 如果没有传入当前日期,使用今天
|
|
|
+ if (!props.currentDate) {
|
|
|
+ const today = new Date()
|
|
|
+ const year = today.getFullYear()
|
|
|
+ const month = (today.getMonth() + 1).toString().padStart(2, '0')
|
|
|
+ const day = today.getDate().toString().padStart(2, '0')
|
|
|
+ const todayStr = `${year}-${month}-${day}`
|
|
|
+
|
|
|
+ currentTime.value = todayStr
|
|
|
+ emit('timeChange', todayStr)
|
|
|
+
|
|
|
+ // 设置默认滚动到最右边(当前日期位置)
|
|
|
+ nextTick(() => {
|
|
|
+ // 计算需要滚动的距离,让最右边(当前月份)显示在屏幕中间
|
|
|
+ // 总宽度2400rpx,屏幕宽度750rpx,要让最右边显示在中间
|
|
|
+ scrollLeft.value = 2400 - 750
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 如果有传入日期,初始化时间
|
|
|
+ currentTime.value = props.currentDate
|
|
|
+
|
|
|
+ // 设置默认滚动到传入日期位置
|
|
|
+ nextTick(() => {
|
|
|
+ const targetPosition = calculatePositionByDate(props.currentDate)
|
|
|
+ // 计算需要滚动的距离,让目标日期显示在屏幕中间
|
|
|
+ scrollLeft.value = targetPosition - 375
|
|
|
+ })
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.time-scale-container {
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-scale-scroll {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 120rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-scale-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.time-scale {
|
|
|
+ display: inline-block;
|
|
|
+ min-width: 2400rpx; // 12个月 * 200rpx
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+ padding: 0 375rpx; // 左右各留一半屏幕宽度,确保最左和最右都在中间
|
|
|
+}
|
|
|
+
|
|
|
+.month-labels {
|
|
|
+ position: relative;
|
|
|
+ height: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.month-label {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #777777;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.timeline {
|
|
|
+ position: relative;
|
|
|
+ height: 80rpx;
|
|
|
+ margin-top: 20rpx;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ height: 2rpx;
|
|
|
+ background: rgba(136, 136, 136, 0.1);
|
|
|
+ transform: translateY(-50%);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.month-dot {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 14rpx;
|
|
|
+ height: 14rpx;
|
|
|
+ background: #777777;
|
|
|
+ border-radius: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ z-index: 5;
|
|
|
+}
|
|
|
+
|
|
|
+.tick-mark {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ width: 2rpx;
|
|
|
+ height: 8rpx;
|
|
|
+ background: rgba(136, 136, 136, 0.3);
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+}
|
|
|
+
|
|
|
+.center-line {
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ width: 2rpx;
|
|
|
+ background: #2199F8;
|
|
|
+ z-index: 15;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.current-time {
|
|
|
+ text-align: center;
|
|
|
+ margin-top: 16rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #2199F8;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+</style>
|