parent
654d641547
commit
2aea269e9b
@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<div class="future-kline-query">
|
||||
<el-card>
|
||||
<el-form :model="queryForm" inline>
|
||||
<el-form-item label="期货代码">
|
||||
<el-input v-model="queryForm.code" placeholder="如: IF2401.CFE, IC2401.CFE" style="width: 150px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期">
|
||||
<el-date-picker
|
||||
v-model="queryForm.startDate"
|
||||
type="date"
|
||||
placeholder="开始日期"
|
||||
value-format="YYYYMMDD"
|
||||
style="width: 140px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期">
|
||||
<el-date-picker
|
||||
v-model="queryForm.endDate"
|
||||
type="date"
|
||||
placeholder="结束日期"
|
||||
value-format="YYYYMMDD"
|
||||
style="width: 140px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="周期">
|
||||
<el-select v-model="queryForm.period" style="width: 100px;">
|
||||
<el-option label="日线" value="daily" />
|
||||
<el-option label="1分钟" value="min1" />
|
||||
<el-option label="5分钟" value="min5" />
|
||||
<el-option label="15分钟" value="min15" />
|
||||
<el-option label="30分钟" value="min30" />
|
||||
<el-option label="60分钟" value="min60" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery" :loading="loading">
|
||||
<el-icon><Search /></el-icon> 查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="chart-card" v-if="chartData.categoryData.length > 0">
|
||||
<template #header>
|
||||
<span>期货K线图 - {{ queryForm.code }}</span>
|
||||
</template>
|
||||
<div ref="chartRef" class="kline-chart"></div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="data-card" v-if="tableData.length > 0">
|
||||
<template #header>
|
||||
<span>数据列表</span>
|
||||
</template>
|
||||
<el-table :data="tableData" stripe height="300">
|
||||
<el-table-column prop="date" label="日期" width="120" />
|
||||
<el-table-column prop="open" label="开盘" :formatter="formatNumber" />
|
||||
<el-table-column prop="high" label="最高" :formatter="formatNumber" />
|
||||
<el-table-column prop="low" label="最低" :formatter="formatNumber" />
|
||||
<el-table-column prop="close" label="收盘" :formatter="formatNumber" />
|
||||
<el-table-column prop="volume" label="成交量" :formatter="formatVolume" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
import { getFutureKlineChart } from '@/api/future'
|
||||
|
||||
const loading = ref(false)
|
||||
const chartRef = ref<HTMLElement>()
|
||||
let chartInstance: echarts.ECharts | null = null
|
||||
|
||||
const queryForm = reactive({
|
||||
code: 'IF2401.CFE',
|
||||
startDate: getDefaultStartDate(),
|
||||
endDate: getDefaultEndDate(),
|
||||
period: 'daily'
|
||||
})
|
||||
|
||||
const chartData = reactive({
|
||||
categoryData: [] as string[],
|
||||
values: [] as number[][],
|
||||
volumes: [] as number[][]
|
||||
})
|
||||
|
||||
const tableData = ref<any[]>([])
|
||||
|
||||
function getDefaultStartDate() {
|
||||
const date = new Date()
|
||||
date.setMonth(date.getMonth() - 3)
|
||||
return formatDate(date)
|
||||
}
|
||||
|
||||
function getDefaultEndDate() {
|
||||
return formatDate(new Date())
|
||||
}
|
||||
|
||||
function formatDate(date: Date) {
|
||||
return date.toISOString().slice(0, 10).replace(/-/g, '')
|
||||
}
|
||||
|
||||
function formatNumber(row: any, column: any, value: number) {
|
||||
return value?.toFixed(2) || '-'
|
||||
}
|
||||
|
||||
function formatVolume(row: any, column: any, value: number) {
|
||||
if (!value) return '-'
|
||||
if (value >= 10000) {
|
||||
return (value / 10000).toFixed(2) + '万'
|
||||
}
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
const handleQuery = async () => {
|
||||
if (!queryForm.code) {
|
||||
ElMessage.warning('请输入期货代码')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await getFutureKlineChart(queryForm.code, {
|
||||
start_date: queryForm.startDate,
|
||||
end_date: queryForm.endDate,
|
||||
period: queryForm.period
|
||||
})
|
||||
|
||||
if (res.data) {
|
||||
chartData.categoryData = res.data.categoryData || []
|
||||
chartData.values = res.data.values || []
|
||||
chartData.volumes = res.data.volumes || []
|
||||
|
||||
tableData.value = chartData.categoryData.map((date, index) => ({
|
||||
date,
|
||||
open: chartData.values[index]?.[0],
|
||||
close: chartData.values[index]?.[1],
|
||||
low: chartData.values[index]?.[2],
|
||||
high: chartData.values[index]?.[3],
|
||||
volume: chartData.values[index]?.[4]
|
||||
})).reverse()
|
||||
|
||||
nextTick(() => {
|
||||
renderChart()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('查询失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const renderChart = () => {
|
||||
if (!chartRef.value) return
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' }
|
||||
},
|
||||
grid: [
|
||||
{ left: '10%', right: '8%', height: '50%' },
|
||||
{ left: '10%', right: '8%', top: '68%', height: '16%' }
|
||||
],
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: chartData.categoryData,
|
||||
scale: true,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
splitLine: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
gridIndex: 1,
|
||||
data: chartData.categoryData,
|
||||
scale: true,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false },
|
||||
axisLabel: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
scale: true,
|
||||
splitArea: { show: true }
|
||||
},
|
||||
{
|
||||
scale: true,
|
||||
gridIndex: 1,
|
||||
splitNumber: 2,
|
||||
axisLabel: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false }
|
||||
}
|
||||
],
|
||||
dataZoom: [
|
||||
{ type: 'inside', xAxisIndex: [0, 1], start: 50, end: 100 },
|
||||
{ show: true, xAxisIndex: [0, 1], type: 'slider', top: '85%', start: 50, end: 100 }
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'K线',
|
||||
type: 'candlestick',
|
||||
data: chartData.values,
|
||||
itemStyle: {
|
||||
color: '#ef232a',
|
||||
color0: '#14b143',
|
||||
borderColor: '#ef232a',
|
||||
borderColor0: '#14b143'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '成交量',
|
||||
type: 'bar',
|
||||
xAxisIndex: 1,
|
||||
yAxisIndex: 1,
|
||||
data: chartData.volumes
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
chartInstance?.resize()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.future-kline-query {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.kline-chart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="stock-kline-query">
|
||||
<el-card>
|
||||
<el-form :model="queryForm" inline>
|
||||
<el-form-item label="股票代码">
|
||||
<el-input v-model="queryForm.code" placeholder="如: 600000.SH, 000001.SZ" style="width: 150px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期">
|
||||
<el-date-picker
|
||||
v-model="queryForm.startDate"
|
||||
type="date"
|
||||
placeholder="开始日期"
|
||||
value-format="YYYYMMDD"
|
||||
style="width: 140px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期">
|
||||
<el-date-picker
|
||||
v-model="queryForm.endDate"
|
||||
type="date"
|
||||
placeholder="结束日期"
|
||||
value-format="YYYYMMDD"
|
||||
style="width: 140px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="周期">
|
||||
<el-select v-model="queryForm.period" style="width: 100px;">
|
||||
<el-option label="日线" value="daily" />
|
||||
<el-option label="1分钟" value="min1" />
|
||||
<el-option label="5分钟" value="min5" />
|
||||
<el-option label="15分钟" value="min15" />
|
||||
<el-option label="30分钟" value="min30" />
|
||||
<el-option label="60分钟" value="min60" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery" :loading="loading">
|
||||
<el-icon><Search /></el-icon> 查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="chart-card" v-if="chartData.categoryData.length > 0">
|
||||
<template #header>
|
||||
<span>股票K线图 - {{ queryForm.code }}</span>
|
||||
</template>
|
||||
<div ref="chartRef" class="kline-chart"></div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="data-card" v-if="tableData.length > 0">
|
||||
<template #header>
|
||||
<span>数据列表</span>
|
||||
</template>
|
||||
<el-table :data="tableData" stripe height="300">
|
||||
<el-table-column prop="date" label="日期" width="120" />
|
||||
<el-table-column prop="open" label="开盘" :formatter="formatNumber" />
|
||||
<el-table-column prop="high" label="最高" :formatter="formatNumber" />
|
||||
<el-table-column prop="low" label="最低" :formatter="formatNumber" />
|
||||
<el-table-column prop="close" label="收盘" :formatter="formatNumber" />
|
||||
<el-table-column prop="volume" label="成交量" :formatter="formatVolume" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
import { getStockKlineChart } from '@/api/stock'
|
||||
|
||||
const loading = ref(false)
|
||||
const chartRef = ref<HTMLElement>()
|
||||
let chartInstance: echarts.ECharts | null = null
|
||||
|
||||
const queryForm = reactive({
|
||||
code: '600000.SH',
|
||||
startDate: getDefaultStartDate(),
|
||||
endDate: getDefaultEndDate(),
|
||||
period: 'daily'
|
||||
})
|
||||
|
||||
const chartData = reactive({
|
||||
categoryData: [] as string[],
|
||||
values: [] as number[][],
|
||||
volumes: [] as number[][]
|
||||
})
|
||||
|
||||
const tableData = ref<any[]>([])
|
||||
|
||||
function getDefaultStartDate() {
|
||||
const date = new Date()
|
||||
date.setFullYear(date.getFullYear() - 1)
|
||||
return formatDate(date)
|
||||
}
|
||||
|
||||
function getDefaultEndDate() {
|
||||
return formatDate(new Date())
|
||||
}
|
||||
|
||||
function formatDate(date: Date) {
|
||||
return date.toISOString().slice(0, 10).replace(/-/g, '')
|
||||
}
|
||||
|
||||
function formatNumber(row: any, column: any, value: number) {
|
||||
return value?.toFixed(2) || '-'
|
||||
}
|
||||
|
||||
function formatVolume(row: any, column: any, value: number) {
|
||||
if (!value) return '-'
|
||||
if (value >= 100000000) {
|
||||
return (value / 100000000).toFixed(2) + '亿'
|
||||
}
|
||||
if (value >= 10000) {
|
||||
return (value / 10000).toFixed(2) + '万'
|
||||
}
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
const handleQuery = async () => {
|
||||
if (!queryForm.code) {
|
||||
ElMessage.warning('请输入股票代码')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await getStockKlineChart(queryForm.code, {
|
||||
start_date: queryForm.startDate,
|
||||
end_date: queryForm.endDate,
|
||||
period: queryForm.period
|
||||
})
|
||||
|
||||
if (res.data) {
|
||||
chartData.categoryData = res.data.categoryData || []
|
||||
chartData.values = res.data.values || []
|
||||
chartData.volumes = res.data.volumes || []
|
||||
|
||||
tableData.value = chartData.categoryData.map((date, index) => ({
|
||||
date,
|
||||
open: chartData.values[index]?.[0],
|
||||
close: chartData.values[index]?.[1],
|
||||
low: chartData.values[index]?.[2],
|
||||
high: chartData.values[index]?.[3],
|
||||
volume: chartData.values[index]?.[4]
|
||||
})).reverse()
|
||||
|
||||
nextTick(() => {
|
||||
renderChart()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('查询失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const renderChart = () => {
|
||||
if (!chartRef.value) return
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' }
|
||||
},
|
||||
grid: [
|
||||
{ left: '10%', right: '8%', height: '50%' },
|
||||
{ left: '10%', right: '8%', top: '68%', height: '16%' }
|
||||
],
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: chartData.categoryData,
|
||||
scale: true,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
splitLine: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
gridIndex: 1,
|
||||
data: chartData.categoryData,
|
||||
scale: true,
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false },
|
||||
axisLabel: { show: false },
|
||||
min: 'dataMin',
|
||||
max: 'dataMax'
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
scale: true,
|
||||
splitArea: { show: true }
|
||||
},
|
||||
{
|
||||
scale: true,
|
||||
gridIndex: 1,
|
||||
splitNumber: 2,
|
||||
axisLabel: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
splitLine: { show: false }
|
||||
}
|
||||
],
|
||||
dataZoom: [
|
||||
{ type: 'inside', xAxisIndex: [0, 1], start: 50, end: 100 },
|
||||
{ show: true, xAxisIndex: [0, 1], type: 'slider', top: '85%', start: 50, end: 100 }
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'K线',
|
||||
type: 'candlestick',
|
||||
data: chartData.values,
|
||||
itemStyle: {
|
||||
color: '#ef232a',
|
||||
color0: '#14b143',
|
||||
borderColor: '#ef232a',
|
||||
borderColor0: '#14b143'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '成交量',
|
||||
type: 'bar',
|
||||
xAxisIndex: 1,
|
||||
yAxisIndex: 1,
|
||||
data: chartData.volumes
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
chartInstance?.resize()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stock-kline-query {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.kline-chart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in new issue