|
@@ -10,13 +10,21 @@
|
|
|
</view>
|
|
|
<view class="album-cont">
|
|
|
<view class="time-line-scroll">
|
|
|
- <scroll-view class="time-line-container" scroll-x :scroll-into-view="scrollIntoViewId" scroll-with-animation>
|
|
|
+ <scroll-view class="time-line-container" scroll-x :scroll-into-view="scrollIntoViewId" scroll-with-animation @scroll="handleScroll" @scrollend="handleScrollEnd">
|
|
|
<view class="time-line">
|
|
|
- <view class="time-item" v-for="(item,index) in dateList" :key="index" :id="`d-${item.dateStr}`" @click="handleDateClick(item.dateStr)">
|
|
|
- <text :style="{color:(selectedDateStr===item.dateStr)?'#2199F8':'#777777'}">{{item.display}}</text>
|
|
|
- <view class="dot" :style="{background:(selectedDateStr===item.dateStr)?'#2199F8':'#777777'}"></view>
|
|
|
- <text class="today" v-if="item.isToday">今</text>
|
|
|
- <text v-if="item.showYear" class="year-flag">{{ item.year }}</text>
|
|
|
+ <!-- 月份标记 -->
|
|
|
+ <view class="month-markers">
|
|
|
+ <view class="month-item" v-for="(month, index) in monthList" :key="index">
|
|
|
+ <text class="month-text">{{ month.label }}</text>
|
|
|
+ <view class="month-dot" :class="{'active': month.isCurrent}"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <!-- 日期刻度 -->
|
|
|
+ <view class="date-markers">
|
|
|
+ <view class="date-item" v-for="(date, index) in dateList" :key="index" :class="{'current-date': date.isCurrent}">
|
|
|
+ <text class="date-text" :class="{'current': date.isCurrent}">{{ date.display }}</text>
|
|
|
+ <view class="date-dot" :class="{'current': date.isCurrent}"></view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
@@ -70,14 +78,16 @@
|
|
|
const handleClose = ()=>{
|
|
|
showPopup.value = false
|
|
|
}
|
|
|
+ const monthList = ref([])
|
|
|
const dateList = ref([])
|
|
|
const scrollIntoViewId = ref('')
|
|
|
const selectedDateStr = ref('')
|
|
|
+ const centerDate = ref('')
|
|
|
|
|
|
const formatToDisplay = (dateStr)=>{
|
|
|
// dateStr: YYYY-MM-DD
|
|
|
const [y,m,d] = dateStr.split('-')
|
|
|
- return `${m}/${d}`
|
|
|
+ return `${m}月${d}日`
|
|
|
}
|
|
|
const getTodayStr = ()=>{
|
|
|
const d = new Date()
|
|
@@ -85,74 +95,58 @@
|
|
|
const dd = `${d.getDate()}`.padStart(2,'0')
|
|
|
return `${d.getFullYear()}-${mm}-${dd}`
|
|
|
}
|
|
|
- const fetchHasImageDates = async ()=>{
|
|
|
- if(!props.farmBuyId) return
|
|
|
- try{
|
|
|
- const {data} = await TREE.findHasImagesDate({farmBuyId: props.farmBuyId})
|
|
|
- const today = getTodayStr()
|
|
|
- const arr = Array.isArray(data) ? data : []
|
|
|
- const mapped = arr.map(ds=>{
|
|
|
- const dateStr = (typeof ds === 'string') ? ds.split(' ')[0] : ''
|
|
|
- const isToday = dateStr === today
|
|
|
- const [y,m,d] = dateStr.split('-')
|
|
|
- return {
|
|
|
- dateStr,
|
|
|
- display: dateStr ? formatToDisplay(dateStr) : '',
|
|
|
- isToday,
|
|
|
- year: y,
|
|
|
- month: m,
|
|
|
- day: d
|
|
|
- }
|
|
|
- }).filter(i=>i.dateStr)
|
|
|
- mapped.sort((a,b)=> a.dateStr.localeCompare(b.dateStr))
|
|
|
-
|
|
|
- // 只显示月份第一天和中间/下月第一天
|
|
|
- const filteredDates = []
|
|
|
- let currentMonth = ''
|
|
|
- let currentYear = ''
|
|
|
+
|
|
|
+ const generateTimeLineData = () => {
|
|
|
+ const today = new Date()
|
|
|
+ const currentYear = today.getFullYear()
|
|
|
+ const currentMonth = today.getMonth() + 1
|
|
|
+ const currentDay = today.getDate()
|
|
|
+
|
|
|
+ // 生成月份列表(往前一年)
|
|
|
+ const months = []
|
|
|
+ for(let i = 11; i >= 0; i--) {
|
|
|
+ const date = new Date(currentYear, currentMonth - 1 - i, 1)
|
|
|
+ const month = date.getMonth() + 1
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const isCurrent = month === currentMonth && year === currentYear
|
|
|
|
|
|
- for(let i=0;i<mapped.length;i++){
|
|
|
- const item = mapped[i]
|
|
|
- const monthKey = `${item.year}-${item.month}`
|
|
|
-
|
|
|
- if(monthKey !== currentMonth){
|
|
|
- // 新月份的第一天
|
|
|
- item.showYear = monthKey.split('-')[0] !== currentYear
|
|
|
- filteredDates.push(item)
|
|
|
- currentMonth = monthKey
|
|
|
- currentYear = item.year
|
|
|
- } else {
|
|
|
- // 检查是否是当前月份的中间日期或下个月第一天
|
|
|
- const day = parseInt(item.day)
|
|
|
- if(day === 15 || day === 1){
|
|
|
- item.showYear = false
|
|
|
- filteredDates.push(item)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ months.push({
|
|
|
+ year,
|
|
|
+ month,
|
|
|
+ label: `${month}月`,
|
|
|
+ isCurrent,
|
|
|
+ dateStr: `${year}-${month.toString().padStart(2,'0')}-01`
|
|
|
+ })
|
|
|
+ }
|
|
|
+ monthList.value = months
|
|
|
+
|
|
|
+ // 生成当前月份的日期刻度(每两天一个)
|
|
|
+ const dates = []
|
|
|
+ const daysInMonth = new Date(currentYear, currentMonth, 0).getDate()
|
|
|
+
|
|
|
+ for(let day = 1; day <= daysInMonth; day += 2) {
|
|
|
+ const dateStr = `${currentYear}-${currentMonth.toString().padStart(2,'0')}-${day.toString().padStart(2,'0')}`
|
|
|
+ const isCurrent = day === currentDay
|
|
|
|
|
|
- dateList.value = filteredDates
|
|
|
- if(dateList.value.length){
|
|
|
- const last = dateList.value[dateList.value.length-1]
|
|
|
- selectedDateStr.value = last.dateStr
|
|
|
- // 强制等渲染后再滚动到最右端
|
|
|
- scrollIntoViewId.value = ''
|
|
|
- await nextTick()
|
|
|
- scrollIntoViewId.value = `d-${last.dateStr}`
|
|
|
- }else{
|
|
|
- selectedDateStr.value = ''
|
|
|
- scrollIntoViewId.value = ''
|
|
|
- }
|
|
|
- }catch(e){
|
|
|
- // 静默失败
|
|
|
+ dates.push({
|
|
|
+ dateStr,
|
|
|
+ display: `${currentMonth}月${day}日`,
|
|
|
+ isCurrent,
|
|
|
+ day
|
|
|
+ })
|
|
|
}
|
|
|
+ dateList.value = dates
|
|
|
+
|
|
|
+ // 设置当前日期为选中状态
|
|
|
+ selectedDateStr.value = getTodayStr()
|
|
|
+ centerDate.value = getTodayStr()
|
|
|
}
|
|
|
+
|
|
|
const fetchTreeImages = async (dateStr)=>{
|
|
|
try{
|
|
|
const params = {
|
|
|
page: 1,
|
|
|
limit: 1,
|
|
|
- // treeId: Number(props.sampleId),
|
|
|
treeId: 110939,
|
|
|
date: dateStr,
|
|
|
}
|
|
@@ -170,9 +164,42 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const handleScroll = (e) => {
|
|
|
+ // 滚动中实时计算中间位置
|
|
|
+ const scrollLeft = e.detail.scrollLeft
|
|
|
+ const viewWidth = e.detail.scrollWidth - e.detail.scrollLeft
|
|
|
+
|
|
|
+ // 计算中间位置对应的日期
|
|
|
+ const centerPosition = scrollLeft + viewWidth / 2
|
|
|
+ const itemWidth = 120 // 每个时间项的宽度
|
|
|
+ const estimatedIndex = Math.floor(centerPosition / itemWidth)
|
|
|
+
|
|
|
+ if(dateList.value[estimatedIndex]){
|
|
|
+ centerDate.value = dateList.value[estimatedIndex].dateStr
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleScrollEnd = (e) => {
|
|
|
+ // 滚动停止时获取中间位置的日期并调用接口
|
|
|
+ const scrollLeft = e.detail.scrollLeft
|
|
|
+ const viewWidth = e.detail.scrollWidth - e.detail.scrollLeft
|
|
|
+
|
|
|
+ const centerPosition = scrollLeft + viewWidth / 2
|
|
|
+ const itemWidth = 120
|
|
|
+ const estimatedIndex = Math.floor(centerPosition / itemWidth)
|
|
|
+
|
|
|
+ if(dateList.value[estimatedIndex]){
|
|
|
+ const finalDate = dateList.value[estimatedIndex].dateStr
|
|
|
+ centerDate.value = finalDate
|
|
|
+ selectedDateStr.value = finalDate
|
|
|
+ console.log('滑动停止,中间位置日期:', finalDate)
|
|
|
+ fetchTreeImages(finalDate)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const handleDateClick = (dateStr)=>{
|
|
|
selectedDateStr.value = dateStr
|
|
|
- scrollIntoViewId.value = `d-${dateStr}`
|
|
|
+ centerDate.value = dateStr
|
|
|
fetchTreeImages(dateStr)
|
|
|
}
|
|
|
const photoList = ref([]);
|
|
@@ -182,7 +209,7 @@
|
|
|
async (val) => {
|
|
|
showPopup.value = val;
|
|
|
if(val){
|
|
|
- await fetchHasImageDates()
|
|
|
+ generateTimeLineData()
|
|
|
const today = getTodayStr()
|
|
|
fetchTreeImages(today)
|
|
|
}
|
|
@@ -192,7 +219,7 @@
|
|
|
() => props.farmBuyId,
|
|
|
(val) => {
|
|
|
if(showPopup.value && val){
|
|
|
- fetchHasImageDates()
|
|
|
+ generateTimeLineData()
|
|
|
}
|
|
|
}
|
|
|
)
|
|
@@ -246,6 +273,7 @@
|
|
|
}
|
|
|
.time-line{
|
|
|
display: flex;
|
|
|
+ flex-direction: column;
|
|
|
padding: 0 20rpx;
|
|
|
position: relative;
|
|
|
}
|
|
@@ -260,44 +288,74 @@
|
|
|
transform: translateX(-50%);
|
|
|
pointer-events: none;
|
|
|
}
|
|
|
- .time-item{
|
|
|
- min-width: 120rpx;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #777777;
|
|
|
+
|
|
|
+ .month-markers{
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- position: relative;
|
|
|
- padding: 0 12rpx;
|
|
|
- cursor: pointer;
|
|
|
+ align-items: flex-end;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
|
- &::before{
|
|
|
- content: '';
|
|
|
- position: absolute;
|
|
|
- top: 44%;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- height: 2rpx;
|
|
|
- background: rgba(136, 136, 136, 0.1);
|
|
|
- }
|
|
|
- .dot{
|
|
|
- width: 14rpx;
|
|
|
- height: 14rpx;
|
|
|
- background: #777777;
|
|
|
- border-radius: 50%;
|
|
|
- position: relative;
|
|
|
- z-index: 5;
|
|
|
- }
|
|
|
- .today{
|
|
|
- color: #2199F8;
|
|
|
- margin-top: 8rpx;
|
|
|
+ .month-item{
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ min-width: 120rpx;
|
|
|
+ padding: 0 12rpx;
|
|
|
+
|
|
|
+ .month-text{
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #777777;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .month-dot{
|
|
|
+ width: 16rpx;
|
|
|
+ height: 16rpx;
|
|
|
+ background: #777777;
|
|
|
+ border-radius: 50%;
|
|
|
+
|
|
|
+ &.active{
|
|
|
+ background: #2199F8;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- .year-flag{
|
|
|
- font-size: 18rpx;
|
|
|
- color: #999;
|
|
|
- margin-top: 6rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .date-markers{
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+
|
|
|
+ .date-item{
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ min-width: 120rpx;
|
|
|
+ padding: 0 12rpx;
|
|
|
+
|
|
|
+ .date-text{
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #777777;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+
|
|
|
+ &.current{
|
|
|
+ color: #2199F8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .date-dot{
|
|
|
+ width: 8rpx;
|
|
|
+ height: 8rpx;
|
|
|
+ background: #ccc;
|
|
|
+ border-radius: 50%;
|
|
|
+
|
|
|
+ &.current{
|
|
|
+ background: #2199F8;
|
|
|
+ width: 16rpx;
|
|
|
+ height: 16rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
.swiper-wrap{
|
|
|
position: relative;
|
|
|
padding: 0 20rpx;
|