|
|
<template> <div class="market-temperature"> <div class="container"> <div class="border3"> <section class="chart-section"> <div> <div class="trapezoid"> <span>{{ companyName }}</span> <span>{{ stockCode }}</span> </div> <div ref="KlineCanvs" class="KlineClass"></div> </div> </section> </div>
<div class="border4"> <el-table :data="groupedWDRL" border :row-style="tableRowStyle" header-cell-class-name="table_header" :cell-style="tableCellStyle" :column-width="cellWidth"> <el-table-column v-for="(day, colIndex) in ['一', '二', '三', '四', '五', '六', '日']" :key="colIndex" :label="day"> <template #default="{ $index: rowIndex }"> <div v-if="getDayData(rowIndex, colIndex + 1)"> <p class="WDRL_date"> {{ formatDate(getDayData(rowIndex, colIndex + 1).date) }} <span class="month-display">{{ formatMonth(getDayData(rowIndex, colIndex + 1).date) }}</span> </p> <p class="WDRL_data"> <template v-if="isIndexCode"> <span v-if="getDayData(rowIndex, colIndex + 1).market_temperature"> {{ getDayData(rowIndex, colIndex + 1).market_temperature }} </span> </template> <template v-else> <template v-if="isBothRest(rowIndex, colIndex + 1)">休市</template> <template v-else> <span v-if="getDayData(rowIndex, colIndex + 1).stock_temperature"> {{ getDayData(rowIndex, colIndex + 1).stock_temperature }} </span> <span v-if="shouldShowDivider(rowIndex, colIndex + 1)"> | </span> <span v-if="getDayData(rowIndex, colIndex + 1).market_temperature"> {{ getDayData(rowIndex, colIndex + 1).market_temperature }} </span> </template> </template> </p> </div> <div v-else-if="shouldShowRest(rowIndex, colIndex + 1)"> <p class="WDRL_date">休市</p> </div> </template> </el-table-column> </el-table> </div> </div> </div> </template>
<script setup> import { ref, computed, onMounted, defineExpose, defineProps, onUnmounted, onBeforeUnmount } from 'vue' import * as echarts from 'echarts'
const props = defineProps({ companyName: { type: String, default: '' }, stockCode: { type: String, default: '' } })
const KlineCanvs = ref() const WDRL = ref([]) const klineDataRaw = ref([]) // 用于存储 K 线图数据
let chartInstance = null // 存储图表实例
const indexCodes = ['NDX', 'DJIA', 'SPX', 'STI', 'KLSE', 'TSX', 'N225', 'KS11', 'JKSE', '1A0001', 'HSI', 'I63', 'VNINDE'] const isIndexCode = computed(() => indexCodes.includes(props.code))
// 分组 WDRL 数据
const groupedWDRL = computed(() => { const result = [] for (let i = 0; i < WDRL.value.length; i += 7) { result.push(WDRL.value.slice(i, i + 7)) } return result })
// 获取指定日期的数据
function getDayData(rowIndex, dayIndex) { const weekData = groupedWDRL.value[rowIndex] if (weekData && weekData.length >= dayIndex) { return weekData[dayIndex - 1] || {} } return {} }
// 判断是否显示分隔符
function shouldShowDivider(rowIndex, dayIndex) { const data = getDayData(rowIndex, dayIndex) return data?.market_temperature && data?.stock_temperature }
// 判断是否都休市
function isBothRest(rowIndex, colIndex) { const data = getDayData(rowIndex, colIndex) return data && data.stock_temperature === '休市' && data.market_temperature === '休市' }
// 判断是否显示休市信息
function shouldShowRest(rowIndex, dayIndex) { const data = getDayData(rowIndex, dayIndex) if (data && (data.stock_temperature || data.market_temperature)) return false const flatIndex = rowIndex * 7 + (dayIndex - 1) const targetDay = WDRL.value[flatIndex] if (!targetDay || !targetDay.date) return false const [year, month, day] = targetDay.date.split('/').map(Number) if (!year || !month || !day) return false const dateObj = new Date(year, month - 1, day) const today = new Date() if (dateObj.getMonth() !== today.getMonth() || dateObj.getFullYear() !== today.getFullYear()) return false const weekday = dateObj.getDay() return weekday >= 1 && weekday <= 5 }
// 格式化月份
function formatMonth(dateStr) { if (!dateStr) return '' const month = dateStr.split('/')[1] const map = { '01': '一月', '02': '二月', '03': '三月', '04': '四月', '05': '五月', '06': '六月', '07': '七月', '08': '八月', '09': '九月', 10: '十月', 11: '十一月', 12: '十二月' } return map[month] || '' }
// 格式化日期
function formatDate(dateStr) { if (!dateStr) return '' return dateStr.split('/')[2] }
// 设置表格单元格样式
function tableCellStyle({ row, column, rowIndex, columnIndex }) { const data = getDayData(rowIndex, columnIndex + 1) let value = isIndexCode.value ? Number(data?.market_temperature) : Number(data?.stock_temperature) if (isNaN(value)) return { backgroundColor: '#4b759f', color: 'white' } if (value >= 90) return { backgroundColor: '#BD0000', color: 'white' } else if (value >= 70) return { backgroundColor: '#FF5638', color: 'white' } else if (value >= 50) return { backgroundColor: '#C929E6', color: 'white' } else if (value >= 20) return { backgroundColor: '#00AB00', color: 'white' } else if (value > 0) return { backgroundColor: '#87CEEB', color: 'white' } else return { backgroundColor: '#4b759f', color: 'white' } } function tableRowStyle() { // 动态调整行高
const containerWidth = document.querySelector('.border4')?.offsetWidth || 0; const rowHeight = containerWidth * 0.1; // 根据容器宽度的比例调整行高
return { height: `${rowHeight}px` }; } // 动态计算单元格宽度
const containerWidth = document.querySelector('.border4')?.offsetWidth || 0; const cellWidth = containerWidth / 7; // 初始化图表
function initChart(raw, klineDataRawValue, WDRLValue) { if (!raw || !klineDataRawValue || !WDRLValue) { console.error('initChart: raw, klineDataRawValue or WDRLValue is undefined') return }
// 如果已存在图表实例,先销毁
if (chartInstance) { chartInstance.dispose() chartInstance = null }
// 处理 K 线图数据
const klineData = klineDataRawValue.map(item => { const open = item[1] const close = item[2] const low = item[3] const high = item[4] return [open, close, low, high] })
// 计算K线数据的最小值和最大值
let minPrice = Infinity let maxPrice = -Infinity klineDataRawValue.forEach(item => { const low = item[3] const high = item[4] minPrice = Math.min(minPrice, low) maxPrice = Math.max(maxPrice, high) })
// 计算小于最小值的整数作为y轴最小值
const yAxisMin = Math.floor(minPrice) // 计算大于最大值的整数作为y轴最大值
const yAxisMax = Math.ceil(maxPrice)
// 温度日历
WDRL.value = WDRLValue klineDataRaw.value = klineDataRawValue
const dateLabels = raw.map(item => item[0]) const marketData = raw.map(item => Math.round(item[1])) const stockData = raw.map(item => Math.round(item[2]))
// 创建新的图表实例
chartInstance = echarts.init(KlineCanvs.value) chartInstance.setOption({ tooltip: { trigger: 'axis', axisPointer: { type: 'cross', crossStyle: { color: '#999', width: 1, type: 'dashed' }, lineStyle: { color: '#999', width: 1, type: 'dashed' } }, formatter: function (params) { if (params && params.length > 0) { let result = `日期: ${params[0].name}<br/>` params.forEach(param => { if (param.seriesType === 'candlestick') { const open = param.data[1] const close = param.data[2] const low = param.data[3] const high = param.data[4] result += `${param.seriesName}<br/>开: ${open}<br/>收: ${close}<br/>低: ${low}<br/>高: ${high}<br/>` } else if (param.seriesType === 'line') { result += `${param.seriesName}: ${param.value}<br/>` } }) return result } return '' } }, legend: { data: ['K线', '市场温度', '股票温度'], textStyle: { color: 'white',fontSize: 18 }}, xAxis: { type: 'category', data: dateLabels, axisLine: { lineStyle: { color: '#00BFFF' } }, axisLabel: { color: '#FFFFFF', fontSize: 12, fontWeight: 'bold' }, axisTick: { lineStyle: { color: '#00BFFF' } }, axisPointer: { show: true, type: 'line', lineStyle: { color: '#999', width: 1, type: 'dashed' }, label: { show: true, color: 'black' }, } }, yAxis: [{ min: yAxisMin, max: yAxisMax, axisLine: { lineStyle: { color: '#00FF7F' } }, axisLabel: { color: '#FFFFFF', fontSize: 12, fontWeight: 'bold' }, axisTick: { lineStyle: { color: '#00FF7F' } }, splitLine: { show: false, lineStyle: { color: '#333333', type: 'solid', opacity: 0.3 } }, axisPointer: { show: true, type: 'line', label: { show: true, color: 'black' }, lineStyle: { color: '#999', width: 1, type: 'dashed' } } }, { min: 0, max: 100, position: 'right', axisLabel: { color: '#FFFF00', fontSize: 12, fontWeight: 'bold' }, axisLine: { lineStyle: { color: '#FF1493', width: 2 } }, axisTick: { lineStyle: { color: '#FF1493' } }, splitLine: { show: false, lineStyle: { color: '#444444', type: 'solid', opacity: 0.3 } }, axisPointer: { show: true, type: 'line', lineStyle: { color: '#999', width: 1, type: 'dashed' }, label: { show: true, color: 'black' }, } }], color: ['#f00', 'white'], series: [ { name: 'K线', type: 'candlestick', data: klineData, itemStyle: { normal: {
color: '#00FF00', // 阳线红色
color0: '#FF0000', // 阴线绿色
borderColor: '#00FF00', // 阳线边框红色
borderColor0: '#FF0000' // 阴线边框绿色
} } }, { name: '市场温度', type: 'line', yAxisIndex: 1, data: marketData }, { name: '股票温度', type: 'line', yAxisIndex: 1, data: stockData } ], // 添加 dataZoom 组件
dataZoom: [ { type: 'slider', xAxisIndex: 0, filterMode: 'filter', textStyle: { color: 'white' } }, { type: 'inside', xAxisIndex: 0, filterMode: 'filter' } ] }) // 监听窗口大小变化
const resizeHandler = () => { if (chartInstance) { chartInstance.resize() } } window.addEventListener('resize', resizeHandler)
// 存储resize处理器以便后续清理
if (!window.marketTempResizeHandler) { window.marketTempResizeHandler = resizeHandler } // 初始调整字体大小
adjustCellFontSize() }
// 调整单元格字体大小
function adjustCellFontSize() { const table = document.querySelector('.border4 .el-table') if (table) { const tableWidth = table.offsetWidth const cellWidth = tableWidth / 7 // 假设一周 7 天
const fontSize = Math.min(cellWidth * 0.15, 20) // 根据单元格宽度动态计算字体大小
const dateElements = document.querySelectorAll('.WDRL_date') const dataElements = document.querySelectorAll('.WDRL_data') dateElements.forEach(el => { el.style.fontSize = `${fontSize}px` }) dataElements.forEach(el => { el.style.fontSize = `${fontSize * 0.8}px` }) } } // 组件卸载时清理资源
onBeforeUnmount(() => { // 销毁图表实例
if (chartInstance) { chartInstance.dispose() chartInstance = null } // 移除窗口resize监听器
if (window.marketTempResizeHandler) { window.removeEventListener('resize', window.marketTempResizeHandler) window.marketTempResizeHandler = null } })
defineExpose({ initChart }) </script>
<style scoped> .WDRL_date { margin-top: 2px; text-align: center; font-size: 1.6vw; font-weight: bold; padding-top: 0%; position: relative; }
.month-display { position: absolute; top: 0; right: 0; font-size: 1vw; color: rgb(58, 58, 58); }
.WDRL_data { margin-top: 5px; text-align: center; font-size: 1vw; font-weight: bold; }
.table_header { color: white; background: #2a2a2a; }
.KlineClass { width: 100%; height: 600px; }
.market-temperature { min-height: 100vh; /* background-color: rgb(0, 22, 65); */ }
.container { margin: 0 auto; /* padding: 20px; */ max-width: 80vw; padding-bottom: 10%; }
.border3 { margin-top: 40px; border-radius: 8px; padding: 20px; margin-left: -2rem; width: 100%; height: 100%; }
.border4 { margin-top: 40px; border-radius: 8px; padding: 20px; width: 80%; margin-left: 8%; height: auto; overflow: visible; }
.border4 .el-table { height: auto !important; max-height: none !important; }
.border4 .el-table__body-wrapper { height: auto !important; max-height: none !important; overflow: visible !important; }
.border4 .el-table__body { height: auto !important; }
/* 手机端适配样式 */ @media only screen and (max-width: 768px) { .KlineClass { width: 100%; height: 300px; }
.border4 { margin-top: 0px; border-radius: 8px; padding: 0px; width: 100%; margin-left: 0%; height: auto; overflow: visible; }
.border4 .el-table { height: auto !important; max-height: none !important; }
.border4 .el-table__body-wrapper { height: auto !important; max-height: none !important; overflow: visible !important; }
.border4 .el-table__body { height: auto !important; }
.el-table .hidden-columns { position: absolute; visibility: hidden; z-index: -1; }
.border3 { margin-top: 25px; border-radius: 8px; padding: 20px; margin-left: -13px; width: 100%; height: 100%; }
.WDRL_date { font-size: 4.2vw; }
.month-display { font-size: 1.8vw; }
.WDRL_data { font-size: 3vw; }
.el-table .cell { box-sizing: border-box; line-height: 23px; overflow: hidden; overflow-wrap: break-word; padding: 0 12px; text-overflow: ellipsis; white-space: normal; text-align: center; } } </style>
|