Files
JiaZhiQianYan/components/LCCalendar/LCCalendar.vue
2026-02-04 15:41:06 +08:00

720 lines
24 KiB
Vue
Raw Permalink 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>
<view class="dateC">
<view class="yearMonthC flex">
<view class="btn" @click="clickPreMonth()">
<image class="icon" src="/static/icon/home/conceptCenter/pre.png" mode="widthFix"></image>
</view>
<view class="yearMonth flex1">
<picker mode="date" fields="month">
<view style="display: flex; align-items: center; justify-content: center;">
<image style="width: 26rpx; height: 26rpx; margin-right: 10rpx;"
src="/pagesStock/static/icon/all-icon-2.png" mode="widthFix"></image>
<view style="color: #2B2B2B; font-size: 32rpx; font-weight: bold;">{{selectDateStr}}</view>
</view>
</picker>
</view>
<view class="btn" @click="clickNextMonth()">
<image class="icon" src="/static/icon/home/conceptCenter/next.png" mode="widthFix"></image>
</view>
</view>
<view style="display: grid; grid-template-columns: repeat(7, 1fr); gap: 17rpx; margin: 20rpx 0;">
<view
style="display: flex; align-items: center; justify-content: center; font-size: 24rpx; color: #292621; font-weight: 500;"
v-for="(item,index) in weekList" :key="index">{{item}}</view>
</view>
<view class="monthDateList" style="display: grid; grid-template-columns: repeat(7, 1fr); gap: 17rpx;">
<view class="item" v-for="(item,index) in monthDateList[selectMonthIndex]" :key="index"
@click="clickSelectDate(item, index)">
<block v-if="item.date==selectDateStr">
<view :class="[
'date',
'select',
getZtCountBgClass(getCalendarItemByDate(item.date)?.zt_count),selectedDateKey === item.date ? 'selected-border' : ''
]">
{{item.day}}
<view v-if="index % 7 == 0 || index % 7 == 6" style="color: #999999; font-size: 18rpx;">休市
</view>
<view v-else style="text-align: center;">
<view v-if="getCalendarItemByDate(item.date)?.zt_count > 0">
<view style="font-size: 18rpx;"
:style="{color: getZtCountTextColor(getCalendarItemByDate(item.date)?.zt_count)}">
{{getCalendarItemByDate(item.date)?.zt_count}}
</view>
<!-- 板块名称文字颜色动态设置 -->
<view style="font-size: 16rpx;"
:style="{color: getZtCountTextColor(getCalendarItemByDate(item.date)?.zt_count)}">
{{getCalendarItemByDate(item.date)?.top_sector || '-'}}
</view>
</view>
</view>
</view>
</block>
<block v-else>
<block v-if="!item.isCurrentMonth">
<!-- <view class="date notCurrentMonth">{{item.day}}</view> -->
</block>
<block v-else>
<view :class="[
'date',
getZtCountBgClass(getCalendarItemByDate(item.date)?.zt_count),selectedDateKey === item.date ? 'selected-border' : ''
]">
<view :style="{color: (index % 7 == 0 || index % 7 == 6 ? '#999999' : '#2A2A2A')}">
{{item.day}}
</view>
<view v-if="index % 7 == 0 || index % 7 == 6" style="color: #999999; font-size: 18rpx;">休市
</view>
<view v-else style="text-align: center;">
<view v-if="getCalendarItemByDate(item.date)?.zt_count > 0">
<view style="font-size: 18rpx;"
:style="{color: getZtCountTextColor(getCalendarItemByDate(item.date)?.zt_count)}">
{{getCalendarItemByDate(item.date)?.zt_count}}
</view>
<!-- 板块名称文字颜色动态设置 -->
<view style="font-size: 16rpx;"
:style="{color: getZtCountTextColor(getCalendarItemByDate(item.date)?.zt_count)}">
{{getCalendarItemByDate(item.date)?.top_sector || '-'}}
</view>
</view>
</view>
</view>
</block>
</block>
</view>
</view>
</view>
</template>
<script>
import {
calendarCombinedData
} from '@/request/api'
export default {
name: "LCCalendar",
data() {
return {
weekList: ['日', '一', '二', '三', '四', '五', '六'],
monthDateList: [],
selectMonthIndex: 0, //选中月份下标
selectMonth: '', //选中年月
selectDateStr: '', //选中日期
startDateStr: '', //开始日期
endDateStr: '', //结束日期
selectYear: '',
Month: '',
calendarApiData: [], // 新增:存储接口返回的日历数据
selectedDateKey: '', // 新增选中日期的唯一标识格式YYYY-MM-DD
};
},
created() {
let currentDate = new Date();
// 获取当前年份
let currentYear = currentDate.getFullYear();
let currentMonth = currentDate.getMonth() + 1;
let currentDay = currentDate.getDate();
this.selectMonthIndex = 20 * 12 + currentMonth - 1
this.selectMonth = currentYear + '年' + currentMonth + '月'
//开始日期默认为当前月份第一天
this.startDateStr = currentYear + '-' + (currentMonth > 9 ? currentMonth : ('0' + currentMonth)) + '-' + '01'
//结束日期默认为当前日期
// this.endDateStr = this.selectDateStr = currentYear + '-' + (currentMonth > 9 ? currentMonth : ('0' +
// currentMonth)) + '-' + (currentDay > 9 ? currentDay : ('0' + currentDay))
this.endDateStr = this.selectDateStr =
`${currentYear}-${currentMonth > 9 ? currentMonth : '0' + currentMonth}-${currentDay > 9 ? currentDay : '0' + currentDay}`
this.getYesterdayDateData()
this.generateMonthDateListData()
// 新增:初始化时触发一次事件,传递当天数据给父页面
//this.emitDateChange(currentYear, currentMonth, currentDay, this.getTodayItem(currentYear, currentMonth, currentDay))
this.emitDateChange(
currentYear,
currentMonth,
currentDay,
this.getTodayItem(currentYear, currentMonth, currentDay),
this.getPrevDayItem(currentYear, currentMonth, currentDay) // 新增上一天数据
)
},
mounted() {
this.getCalendarCombinedData()
},
methods: {
// 3. 在日历组件methods中新增getPrevDayItem方法
/**
* 获取指定日期的上一天数据
*/
getPrevDayItem(year, month, day) {
// 计算上一天日期
const currentDate = new Date(`${year}-${month}-${day}`);
const prevDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000);
const prevYear = prevDate.getFullYear();
const prevMonth = prevDate.getMonth() + 1;
const prevDay = prevDate.getDate();
// 获取上一天的完整数据
const targetDate =
`${prevYear}-${prevMonth > 9 ? prevMonth : '0' + prevMonth}-${prevDay > 9 ? prevDay : '0' + prevDay}`;
const currentMonthList = this.monthDateList[this.selectMonthIndex] || [];
const localItem = currentMonthList.find(item => item.date === targetDate) || null;
const apiData = this.getCalendarItemByDate(targetDate) || {};
return {
...localItem,
zt_count: apiData.zt_count || 0,
top_sector: apiData.top_sector || '-',
zaban_rate: apiData.zaban_rate || '0%'
};
},
/**
* 获取当天的item数据合并接口数据
*/
getTodayItem(year, month, day) {
const targetDate = `${year}-${month > 9 ? month : '0' + month}-${day > 9 ? day : '0' + day}`;
const currentMonthList = this.monthDateList[this.selectMonthIndex] || [];
// 先获取本地日期item
const localItem = currentMonthList.find(item => item.date === targetDate) || null;
if (!localItem) return null;
// 获取接口数据并合并
const apiData = this.getCalendarItemByDate(targetDate) || {};
// 合并本地item和接口数据
return {
...localItem,
zt_count: apiData.zt_count || 0,
top_sector: apiData.top_sector || '-',
// 可补充其他接口字段
zaban_rate: apiData.zaban_rate || '0%' // 示例:炸板率
};
},
emitDateChange(year, month, day, item, prevItem = {
zt_count: 0
}) { // 新增prevItem参数设置默认值兜底
const yearMonth = `${year}-${month > 9 ? month : '0' + month}`;
const fullDate = `${year}-${month > 9 ? month : '0' + month}-${day > 9 ? day : '0' + day}`;
this.$emit('date-change', {
yearMonth,
fullDate,
item: item || { // 兜底无当前item时赋值空对象+默认值
date: fullDate,
year,
month,
day,
zt_count: 0,
top_sector: '-',
zaban_rate: '0%'
},
prevItem: prevItem || { // 新增:传递上一天数据,兜底默认值
zt_count: 0,
top_sector: '-',
zaban_rate: '0%'
},
year,
month,
day
});
},
/**
* 获取当前时间前一天的数据
*/
getYesterdayDateData() {
let currentDate = new Date();
let selectDate = new Date(currentDate)
selectDate.setDate(selectDate.getDate() - 1)
let selectYear = selectDate.getFullYear();
let selectMonth = selectDate.getMonth() + 1;
let selectDay = selectDate.getDate();
this.selectYear = selectDate.getFullYear()
this.Month = selectDate.getMonth() + 1;
// this.selectDateStr = selectYear + '-' + (selectMonth > 9 ? selectMonth : ('0' + selectMonth)) + '-' + (
// selectDay > 9 ? selectDay : ('0' + selectDay))
//this.selectDateStr = selectYear + '-' + (selectMonth > 9 ? selectMonth : ('0' + selectMonth))
},
/**
* 根据日期查找接口中的日历数据
* @param {String} dateStr 格式2026-01-01
* @returns {Object} 匹配的日历数据
*/
getCalendarItemByDate(dateStr) {
if (!dateStr || !this.calendarApiData.length) return null;
// 格式化本地日期2026-01-01 → 20260101以匹配接口格式
const formatDate = dateStr.replace(/-/g, '');
// 查找匹配的数据项
return this.calendarApiData.find(item => item.date === formatDate) || null;
},
/**
* 根据zt_count和是否休市获取背景色样式类
* @param {Number} count zt_count数值
* @param {Number} index 日期下标(判断是否休市)
* @returns {String} 样式类名
*/
getZtCountBgClass(count, index) {
// 休市(周日/周六或zt_count为0/null/undefined → 默认样式
if ((index % 7 === 0 || index % 7 === 6) || count === 0 || count === null || count === undefined) {
return '';
}
// 按数值范围返回对应样式类
if (count >= 80) return 'zt-bg-80';
if (count >= 60) return 'zt-bg-60';
if (count >= 40) return 'zt-bg-40';
return 'zt-bg-40-less';
},
/**
* 根据zt_count值获取文字颜色
* @param {Number} count zt_count数值
* @param {Number} index 日期下标(用于判断周末)
* @returns {String} 文字颜色值
*/
getZtCountTextColor(count, index) {
// 周末强制显示#999999
if (index !== undefined && (index % 7 === 0 || index % 7 === 6)) {
return '#999999';
}
// 无数据或0值显示默认颜色
if (count === undefined || count === null || count === 0) {
return '#2A2A2A';
}
// 根据数值范围返回对应颜色
if (count >= 80) return '#5D288F';
if (count >= 60) return '#BE1B1B';
if (count >= 40) return '#F59B38';
return '#2958AA';
},
/**
* 获取日历接口数据
*/
async getCalendarCombinedData() {
try {
let param = {
year: this.selectYear,
month: this.Month
}
const res = await calendarCombinedData(param);
if (res.success && Array.isArray(res.data)) {
this.calendarApiData = res.data;
//console.log('日历数据加载成功', this.calendarApiData);
// 接口数据加载后,重新触发一次当前选中日期的事件,更新数据
if (this.selectDateStr) {
const [year, month, day] = this.selectDateStr.split('-').map(Number);
//this.emitDateChange(year, month, day, this.getTodayItem(year, month, day));
// this.emitDateChange(
// year,
// month,
// day,
// this.getTodayItem(year, month, day),
// this.getPrevDayItem(year, month, day) // 新增上一天数据
// )
}
} else {
this.calendarApiData = [];
console.warn('日历接口返回数据格式异常', res);
}
} catch (error) {
this.calendarApiData = [];
console.error('获取日历数据失败', error);
}
},
/**
* 生成日期数组
*/
generateMonthDateListData() {
let currentDate = new Date();
// 获取当前年份
let currentYear = currentDate.getFullYear();
let currentMonth = currentDate.getMonth() + 1;
let currentDay = currentDate.getDate();
let monthDateList = []
for (var i = currentYear - 20; i < currentYear + 20; i++) {
for (var j = 0; j < 12; j++) {
let date = new Date(i, j + 1, 0)
let firstDayOfMonth = new Date(i, j + 1, 0);
firstDayOfMonth.setDate(1);
//获取当前月天数
let currentMonthDay = date.getDate()
let firstDayWeek = firstDayOfMonth.getDay() + 1
let daysOfMonth = []
for (var k = 1; k <= currentMonthDay; k++) {
let newDate = new Date(i, j + 1, 0)
newDate.setDate(k); // 设置日期为当前月的相应日期
let newMonth = newDate.getMonth() + 1
let newDay = newDate.getDate()
let time = newDate.getTime()
let date = i + '-' + (newMonth > 9 ? newMonth : ('0' + newMonth)) + '-' + (newDay > 9 ?
newDay : ('0' + newDay))
daysOfMonth.push({
date: date,
year: i,
month: newMonth,
day: newDay,
isToday: (i == currentYear && newMonth == currentMonth && newDay == currentDay) ?
true : false,
isCurrentMonth: true,
isLastDay: newDay == currentMonthDay ? true : false,
timestamp: time
});
}
for (var k = 0; k < firstDayWeek - 1; k++) {
//获取上月天数
let year = i
let month = j
if (j < 1) {
year = i - 1
month = 12
}
let lastMonthDay = new Date(year, month, 0).getDate()
//获取上月日期
let newDate = new Date(year, month - 1, lastMonthDay - k)
let newMonth = newDate.getMonth() + 1
let newDay = newDate.getDate()
let time = newDate.getTime()
let date = year + '-' + (newMonth > 9 ? newMonth : ('0' + newMonth)) + '-' + (newDay > 9 ?
newDay : ('0' + newDay))
daysOfMonth.unshift({
date: date,
year: year,
month: newMonth,
day: newDay,
isToday: false,
isCurrentMonth: false,
isLastDay: false,
timestamp: time
});
}
// 下一个月的第一天
let nextMonthFirstDay = new Date(i, j + 1, 1);
// 然后减去一天就是当前月的最后一天
let lastDayOfMonth = new Date(nextMonthFirstDay - (24 * 60 * 60 * 1000)); // 减去一天的毫秒数
let lastDayWeek = lastDayOfMonth.getDay() + 1; // 返回0星期天到6星期六
for (var k = 1; k < 8 - lastDayWeek; k++) {
let year = i
let month = j
if (month > 11) {
month = 0
year++
}
//获取下月日期
let newDate = new Date(year, month + 1, k)
let newMonth = newDate.getMonth() + 1
let newDay = newDate.getDate()
let time = newDate.getTime()
let date = year + '-' + (newMonth > 9 ? newMonth : ('0' + newMonth)) + '-' + (newDay > 9 ?
newDay : ('0' + newDay))
daysOfMonth.push({
date: date,
year: year,
month: newMonth,
day: newDay,
isToday: false,
isCurrentMonth: false,
isLastDay: false,
timestamp: time
});
}
monthDateList.push(daysOfMonth)
}
}
this.monthDateList = monthDateList
},
/**
* 点击上个月
*/
clickPreMonth() {
if (this.selectMonthIndex > 0) {
// 新增:切换月份时清空选中标识
this.selectedDateKey = '';
this.selectMonthIndex--
let monthList = this.monthDateList[this.selectMonthIndex]
let year = ''
let month = ''
for (let item of monthList) {
if (item.isCurrentMonth) {
year = item.year
month = item.month
break
}
}
let lastDay = ''
for (let item of monthList) {
if (item.isLastDay) {
lastDay = item.day
break
}
}
this.selectMonth = year + '年' + month + '月'
this.startDateStr = year + '-' + (month > 9 ? month : ('0' + month)) + '-' + '01'
this.endDateStr = year + '-' + (month > 9 ? month : ('0' + month)) + '-' + lastDay
this.selectYear = year
this.Month = month;
this.selectDateStr =this.startDateStr ;
this.getCalendarCombinedData()
console.log('点击上个月');
}
},
/**
* 点击下个月
*/
clickNextMonth() {
if (this.selectMonthIndex < this.monthDateList.length - 1) {
// 新增:切换月份时清空选中标识
this.selectedDateKey = '';
this.selectMonthIndex++
let monthList = this.monthDateList[this.selectMonthIndex]
let year = ''
let month = ''
for (let item of monthList) {
if (item.isCurrentMonth) {
year = item.year
month = item.month
break
}
}
let lastDay = ''
for (let item of monthList) {
if (item.isLastDay) {
lastDay = item.day
break
}
}
this.selectMonth = year + '年' + month + '月'
this.startDateStr = year + '-' + (month > 9 ? month : ('0' + month)) + '-' + '01'
this.endDateStr = year + '-' + (month > 9 ? month : ('0' + month)) + '-' + lastDay
console.log('点击下个月');
this.selectYear = year
this.Month = month;
this.selectDateStr =this.startDateStr ; // 统一格式 YYYY-MM
this.getCalendarCombinedData()
}
},
/**
* 通用补零函数确保数字为两位数不足则前面补0
* @param {number|string} num - 需要补零的数字
* @returns {string} 补零后的字符串
*/
padZero(num) {
return String(num).padStart(2, '0');
},
monthChange(e) {
let currentDate = new Date();
//当前年份
let currentYear = currentDate.getFullYear()
//选中年月
let yearMonth = e.detail.value
let selectYear = parseInt(yearMonth.split('-')[0])
let selectMonth = parseInt(yearMonth.split('-')[1])
this.selectMonthIndex = (selectYear - (currentYear - 20)) * 12 + selectMonth - 1
this.selectMonth = selectYear + '年' + selectMonth + '月'
this.startDateStr = selectYear + '-' + (selectMonth > 9 ? selectMonth : ('0' + selectMonth)) + '-' + '01'
let lastDayOfMonth = new Date(selectYear, selectMonth, 0);
this.endDateStr = selectYear + '-' + (selectMonth > 9 ? selectMonth : ('0' + selectMonth)) + '-' +
lastDayOfMonth.getDate()
console.log('月份变更');
// 月份切换时更新接口数据
this.selectYear = selectYear;
this.Month = selectMonth;
this.getCalendarCombinedData();
},
/**
* 点击选择开始日期和结束日期
* @param {Object} item
*/
clickSelectDate(item, index) { // 新增index参数
if (!item.isCurrentMonth) return
if (this.selectDateStr != item.date) {
this.selectedDateKey = item.date;
this.selectDateStr = item.date
// 1. 获取该日期的接口数据
const apiData = this.getCalendarItemByDate(item.date) || {};
// 2. 合并本地item和接口数据补充默认值
const mergedItem = {
...item, // 本地日期基础数据
zt_count: apiData.zt_count || 0, // 涨停家数默认0
top_sector: apiData.top_sector || '-', // 热门板块,默认'-'
zaban_rate: apiData.zaban_rate || '0%', // 炸板率,示例字段
isWeekend: index % 7 === 0 || index % 7 === 6 // 是否周末
};
// ===== 新增:获取上一天的日期和数据 =====
// 2.1 计算上一天的日期
const currentDate = new Date(item.date);
const prevDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000); // 减一天
const prevYear = prevDate.getFullYear();
const prevMonth = prevDate.getMonth() + 1;
const prevDay = prevDate.getDate();
const prevDateStr =
`${prevYear}-${prevMonth > 9 ? prevMonth : '0' + prevMonth}-${prevDay > 9 ? prevDay : '0' + prevDay}`;
// 2.2 获取上一天的接口数据
const prevApiData = this.getCalendarItemByDate(prevDateStr) || {};
// 2.3 查找上一天的本地item用于补全基础信息
let prevLocalItem = null;
// 先在当前月份列表找
const currentMonthList = this.monthDateList[this.selectMonthIndex] || [];
prevLocalItem = currentMonthList.find(i => i.date === prevDateStr);
// 如果当前月份没有(跨月),找对应月份的列表
if (!prevLocalItem) {
const prevMonthIndex = this.selectMonthIndex - (prevMonth < item.month ? 1 : 0);
const prevMonthList = this.monthDateList[prevMonthIndex] || [];
prevLocalItem = prevMonthList.find(i => i.date === prevDateStr);
}
// 2.4 合并上一天的完整数据
const prevMergedItem = {
...(prevLocalItem || {}),
zt_count: prevApiData.zt_count || 0,
top_sector: prevApiData.top_sector || '-',
zaban_rate: prevApiData.zaban_rate || '0%',
isWeekend: false // 兜底默认值
};
// =======================================
this.chgStockData = mergedItem;
// 3. 解析日期,触发事件传递【当前数据+上一天数据】
const [year, month, day] = item.date.split('-').map(Number);
this.emitDateChange(year, month, day, mergedItem, prevMergedItem); // 新增传递上一天数据
console.log('点击某天(含接口数据)', {
current: mergedItem,
prev: prevMergedItem
});
}
}
}
}
</script>
<style lang="less">
.dateC {
background-color: white;
box-shadow: 0 5rpx 10rpx 0 rgba(127, 127, 127, 0.1);
box-sizing: border-box;
.yearMonthC {
// background-color: #F7F7F7;
height: 70rpx;
border-radius: 35rpx;
.btn {
padding: 0 32rpx;
.icon {
width: 13rpx;
height: auto;
}
}
.yearMonth {
font-size: 32rpx;
font-weight: 500;
color: #070707;
text-align: center;
}
}
.weekList {
.item {
line-height: 72rpx;
font-size: 26rpx;
font-weight: 500;
color: #A7A7A7;
text-align: center;
}
}
.monthDateList {
.item {
.date {
background-color: #f8f8f8;
padding: 10rpx 0;
border-radius: 10rpx;
font-size: 26rpx;
font-weight: bold;
color: #2A2A2A;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100%;
.chg {
font-size: 18rpx;
}
.chg.up {
color: #EC3440;
}
.chg.down {
color: #38A169;
}
&.selected-border {
border: 1px solid #FFCC00; // 黄色边框(可根据需求调整色值)
box-sizing: border-box; // 保证边框不超出容器
}
}
// zt_count ≥80 背景色
.date.zt-bg-80 {
background-color: #FAEEFF;
}
// zt_count ≥60 背景色
.date.zt-bg-60 {
background-color: #FFE9E9;
}
// zt_count ≥40 背景色
.date.zt-bg-40 {
background-color: #FFF8F0;
}
// zt_count <40 背景色
.date.zt-bg-40-less {
background-color: #EEF4FF;
}
.date.up {
background-color: #FFD6D9;
}
.date.down {
background-color: #CEF1DE;
}
.date.select.up {
background-color: #EC3440;
color: white;
.chg {
color: white;
}
}
.date.select.down {
background-color: #38A169;
color: white;
.chg {
color: white;
}
}
.date.notCurrentMonth {
background-color: #FCFCFC;
color: #999;
}
}
}
}
</style>