更新ios
This commit is contained in:
@@ -31,7 +31,7 @@ import ztService from '../../services/ztService';
|
||||
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||
const CELL_WIDTH = (SCREEN_WIDTH - 32) / 7;
|
||||
const CELL_HEIGHT = 90; // 增加高度以容纳更多内容
|
||||
const CELL_HEIGHT = 95; // 增加高度以容纳跨天概念条
|
||||
|
||||
// 概念颜色调色板
|
||||
const CONCEPT_COLORS = [
|
||||
@@ -72,26 +72,32 @@ const formatDateStr = (year, month, day) => {
|
||||
return `${year}${m}${d}`;
|
||||
};
|
||||
|
||||
// 检查是否连续日期(跳过周末)
|
||||
const isConsecutiveDate = (date1, date2) => {
|
||||
const d1 = new Date(
|
||||
parseInt(date1.slice(0, 4)),
|
||||
parseInt(date1.slice(4, 6)) - 1,
|
||||
parseInt(date1.slice(6, 8))
|
||||
);
|
||||
const d2 = new Date(
|
||||
parseInt(date2.slice(0, 4)),
|
||||
parseInt(date2.slice(4, 6)) - 1,
|
||||
parseInt(date2.slice(6, 8))
|
||||
// 解析日期字符串为 Date 对象
|
||||
const parseDateStr = (dateStr) => {
|
||||
return new Date(
|
||||
parseInt(dateStr.slice(0, 4)),
|
||||
parseInt(dateStr.slice(4, 6)) - 1,
|
||||
parseInt(dateStr.slice(6, 8))
|
||||
);
|
||||
};
|
||||
|
||||
// 检查是否是下一个交易日(跳过周末)
|
||||
const isNextTradingDay = (date1, date2) => {
|
||||
const d1 = parseDateStr(date1);
|
||||
const d2 = parseDateStr(date2);
|
||||
const diff = (d2 - d1) / (1000 * 60 * 60 * 24);
|
||||
|
||||
// 直接相邻
|
||||
if (diff === 1) return true;
|
||||
if (diff === 2 && d1.getDay() === 5) return true; // 周五到周日
|
||||
if (diff === 3 && d1.getDay() === 5) return true; // 周五到周一
|
||||
// 周五到周一(跳过周末)
|
||||
if (diff === 3 && d1.getDay() === 5) return true;
|
||||
// 周六到周一
|
||||
if (diff === 2 && d1.getDay() === 6) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 合并连续相同概念
|
||||
// 合并连续相同概念(跨周显示由分段逻辑处理)
|
||||
const mergeConsecutiveConcepts = (calendarData, year, month) => {
|
||||
const sorted = [...calendarData]
|
||||
.filter(d => d.topSector)
|
||||
@@ -102,17 +108,22 @@ const mergeConsecutiveConcepts = (calendarData, year, month) => {
|
||||
|
||||
sorted.forEach((item, index) => {
|
||||
const prevItem = sorted[index - 1];
|
||||
const isConsecutive = prevItem &&
|
||||
item.topSector === prevItem.topSector &&
|
||||
isConsecutiveDate(prevItem.date, item.date);
|
||||
|
||||
if (isConsecutive && currentEvent) {
|
||||
// 检查是否应该继续当前事件
|
||||
const shouldContinue = prevItem &&
|
||||
currentEvent &&
|
||||
item.topSector === prevItem.topSector &&
|
||||
isNextTradingDay(prevItem.date, item.date);
|
||||
|
||||
if (shouldContinue) {
|
||||
currentEvent.endDate = item.date;
|
||||
currentEvent.dates.push(item.date);
|
||||
} else {
|
||||
// 保存之前的事件(如果有多天)
|
||||
if (currentEvent && currentEvent.dates.length > 1) {
|
||||
events.push(currentEvent);
|
||||
}
|
||||
// 开始新事件
|
||||
currentEvent = {
|
||||
concept: item.topSector,
|
||||
startDate: item.date,
|
||||
@@ -122,6 +133,7 @@ const mergeConsecutiveConcepts = (calendarData, year, month) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 保存最后一个事件
|
||||
if (currentEvent && currentEvent.dates.length > 1) {
|
||||
events.push(currentEvent);
|
||||
}
|
||||
@@ -129,6 +141,56 @@ const mergeConsecutiveConcepts = (calendarData, year, month) => {
|
||||
return events;
|
||||
};
|
||||
|
||||
// 将概念事件分割成多行显示的段(处理跨周情况)
|
||||
// 基于实际的 dates 数组而不是日期范围
|
||||
const splitEventIntoSegments = (event, startingDay, daysInMonth) => {
|
||||
const segments = [];
|
||||
const { dates, concept } = event;
|
||||
|
||||
if (!dates || dates.length === 0) return segments;
|
||||
|
||||
let currentSegment = null;
|
||||
|
||||
dates.forEach((dateStr, idx) => {
|
||||
const day = parseInt(dateStr.slice(6, 8));
|
||||
const col = (startingDay + day - 1) % 7;
|
||||
const row = Math.floor((startingDay + day - 1) / 7);
|
||||
|
||||
// 检查是否应该开始新段
|
||||
// 条件:没有当前段,或者行变化了
|
||||
if (!currentSegment || currentSegment.row !== row) {
|
||||
// 保存之前的段
|
||||
if (currentSegment) {
|
||||
segments.push(currentSegment);
|
||||
}
|
||||
// 开始新段
|
||||
currentSegment = {
|
||||
startDay: day,
|
||||
endDay: day,
|
||||
startCol: col,
|
||||
endCol: col,
|
||||
row: row,
|
||||
totalDays: dates.length,
|
||||
concept: concept,
|
||||
isStart: idx === 0,
|
||||
isEnd: idx === dates.length - 1,
|
||||
};
|
||||
} else {
|
||||
// 扩展当前段
|
||||
currentSegment.endDay = day;
|
||||
currentSegment.endCol = col;
|
||||
currentSegment.isEnd = idx === dates.length - 1;
|
||||
}
|
||||
});
|
||||
|
||||
// 保存最后一个段
|
||||
if (currentSegment) {
|
||||
segments.push(currentSegment);
|
||||
}
|
||||
|
||||
return segments;
|
||||
};
|
||||
|
||||
// 火焰图标组件
|
||||
const FlameIcon = ({ color, size = 14 }) => (
|
||||
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
||||
@@ -222,27 +284,39 @@ const EventCalendar = ({ navigation }) => {
|
||||
navigation.navigate('MarketHot', { date: dateStr });
|
||||
}, [currentYear, currentMonth, navigation]);
|
||||
|
||||
// 计算概念条位置
|
||||
const getConceptBarPosition = (event, rowIndex) => {
|
||||
const startDay = parseInt(event.startDate.slice(6, 8));
|
||||
const endDay = parseInt(event.endDate.slice(6, 8));
|
||||
// 获取所有概念条的显示段
|
||||
const conceptSegments = useMemo(() => {
|
||||
const allSegments = [];
|
||||
|
||||
// 计算在网格中的位置
|
||||
const startCol = (startingDay + startDay - 1) % 7;
|
||||
const startRow = Math.floor((startingDay + startDay - 1) / 7);
|
||||
const endCol = (startingDay + endDay - 1) % 7;
|
||||
const endRow = Math.floor((startingDay + endDay - 1) / 7);
|
||||
conceptEvents.forEach((event, eventIndex) => {
|
||||
const segments = splitEventIntoSegments(event, startingDay, daysInMonth);
|
||||
segments.forEach((segment, segIndex) => {
|
||||
allSegments.push({
|
||||
...segment,
|
||||
eventIndex,
|
||||
segmentIndex: segIndex,
|
||||
color: getConceptColor(event.concept),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 只处理同一行的情况(简化版)
|
||||
if (startRow !== endRow) return null;
|
||||
if (startRow !== rowIndex) return null;
|
||||
// 按行分组,用于计算同一行内的堆叠
|
||||
const byRow = {};
|
||||
allSegments.forEach(seg => {
|
||||
if (!byRow[seg.row]) byRow[seg.row] = [];
|
||||
byRow[seg.row].push(seg);
|
||||
});
|
||||
|
||||
return {
|
||||
left: startCol * CELL_WIDTH + 4,
|
||||
width: (endCol - startCol + 1) * CELL_WIDTH - 8,
|
||||
row: startRow,
|
||||
};
|
||||
};
|
||||
// 为每个段分配垂直偏移(同一行内多个概念条时)
|
||||
Object.values(byRow).forEach(rowSegments => {
|
||||
rowSegments.forEach((seg, idx) => {
|
||||
seg.stackIndex = idx;
|
||||
seg.stackTotal = rowSegments.length;
|
||||
});
|
||||
});
|
||||
|
||||
return allSegments;
|
||||
}, [conceptEvents, startingDay, daysInMonth]);
|
||||
|
||||
// 渲染日历格子
|
||||
const renderCalendarCells = () => {
|
||||
@@ -373,52 +447,58 @@ const EventCalendar = ({ navigation }) => {
|
||||
|
||||
// 渲染概念横条
|
||||
const renderConceptBars = () => {
|
||||
const totalRows = Math.ceil((startingDay + daysInMonth) / 7);
|
||||
const bars = [];
|
||||
return conceptSegments.map((segment, index) => {
|
||||
const { startCol, endCol, row, totalDays, concept, color, isStart, stackIndex } = segment;
|
||||
|
||||
conceptEvents.forEach((event, eventIndex) => {
|
||||
for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
|
||||
const position = getConceptBarPosition(event, rowIndex);
|
||||
if (!position) continue;
|
||||
// 计算位置和尺寸
|
||||
const left = startCol * CELL_WIDTH + 2;
|
||||
const width = (endCol - startCol + 1) * CELL_WIDTH - 4;
|
||||
// 根据堆叠索引调整垂直位置(每个概念条高度 20,间隔 2)
|
||||
const barHeight = 20;
|
||||
const verticalOffset = stackIndex * (barHeight + 2);
|
||||
const top = row * CELL_HEIGHT + CELL_HEIGHT - 26 - verticalOffset;
|
||||
|
||||
const color = getConceptColor(event.concept);
|
||||
const daysCount = event.dates.length;
|
||||
|
||||
bars.push(
|
||||
return (
|
||||
<Box
|
||||
key={`${event.concept}-${event.startDate}-${rowIndex}`}
|
||||
key={`${concept}-${segment.startDay}-${row}-${index}`}
|
||||
position="absolute"
|
||||
left={position.left}
|
||||
top={rowIndex * CELL_HEIGHT + CELL_HEIGHT - 28}
|
||||
w={position.width}
|
||||
zIndex={10}
|
||||
left={left}
|
||||
top={top}
|
||||
w={width}
|
||||
zIndex={10 + stackIndex}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={color.bg}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.conceptBar}
|
||||
style={[
|
||||
styles.conceptBar,
|
||||
{
|
||||
// 根据是否是起始/结束段调整圆角
|
||||
borderTopLeftRadius: isStart ? 6 : 0,
|
||||
borderBottomLeftRadius: isStart ? 6 : 0,
|
||||
borderTopRightRadius: segment.isEnd ? 6 : 0,
|
||||
borderBottomRightRadius: segment.isEnd ? 6 : 0,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontSize="2xs"
|
||||
fontWeight="bold"
|
||||
color={color.text}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{event.concept}
|
||||
{daysCount > 1 && (
|
||||
{concept}
|
||||
{totalDays > 1 && isStart && (
|
||||
<Text fontSize="2xs" opacity={0.8}>
|
||||
{' '}({daysCount}天)
|
||||
({totalDays}天)
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return bars;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -641,11 +721,10 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 16,
|
||||
},
|
||||
conceptBar: {
|
||||
height: 22,
|
||||
borderRadius: 6,
|
||||
height: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 8,
|
||||
paddingHorizontal: 6,
|
||||
},
|
||||
legendBar: {
|
||||
width: 20,
|
||||
|
||||
@@ -266,34 +266,29 @@ export const ztService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 快速获取日历数据(只从 dates.json 获取基础信息)
|
||||
* 快速获取日历数据(从 API 获取完整信息包括 top_sector)
|
||||
* @param {number} year - 年份
|
||||
* @param {number} month - 月份 (1-12)
|
||||
* @returns {Promise<object>} 日历数据
|
||||
*/
|
||||
getCalendarDataFast: async (year, month) => {
|
||||
try {
|
||||
const datesResult = await ztService.getAvailableDates();
|
||||
if (!datesResult.success) {
|
||||
return { success: false, data: [] };
|
||||
}
|
||||
// 使用后端日历 API 获取包含 top_sector 的完整数据
|
||||
const response = await apiRequest(`/api/zt/calendar?year=${year}&month=${month}`);
|
||||
|
||||
const dates = datesResult.data.dates || [];
|
||||
const monthStr = String(month).padStart(2, '0');
|
||||
const monthPrefix = `${year}${monthStr}`;
|
||||
|
||||
const calendarData = dates
|
||||
.filter(d => d.date.startsWith(monthPrefix))
|
||||
.map(d => ({
|
||||
if (response.success && response.data) {
|
||||
const calendarData = response.data.map(d => ({
|
||||
date: d.date,
|
||||
ztCount: d.count || 0,
|
||||
formattedDate: d.formatted_date,
|
||||
ztCount: d.zt_count || 0,
|
||||
topSector: d.top_sector || '',
|
||||
eventCount: d.event_count || 0,
|
||||
indexChange: d.index_change || null,
|
||||
}));
|
||||
|
||||
return { success: true, data: calendarData };
|
||||
}
|
||||
|
||||
return { success: false, data: [] };
|
||||
} catch (error) {
|
||||
console.error('获取日历数据失败:', error);
|
||||
return { success: false, data: [] };
|
||||
|
||||
Reference in New Issue
Block a user