|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="detect-missing">
|
|
|
|
|
|
<el-card>
|
|
|
|
|
|
<el-form :model="form" label-width="100px">
|
|
|
|
|
|
<el-form-item label="证券类型">
|
|
|
|
|
|
<el-radio-group v-model="form.securityType">
|
|
|
|
|
|
<el-radio-button label="stock">股票</el-radio-button>
|
|
|
|
|
|
<el-radio-button label="future">期货</el-radio-button>
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="周期">
|
|
|
|
|
|
<el-select v-model="form.periodType" style="width: 120px;">
|
|
|
|
|
|
<el-option label="日线" value="daily" />
|
|
|
|
|
|
<el-option label="1分钟" value="min1" />
|
|
|
|
|
|
<el-option label="5分钟" value="min5" />
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="开始日期">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="form.startDate"
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
placeholder="开始日期"
|
|
|
|
|
|
value-format="YYYYMMDD"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="结束日期">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="form.endDate"
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
placeholder="结束日期"
|
|
|
|
|
|
value-format="YYYYMMDD"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="代码列表">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="codeInput"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:rows="4"
|
|
|
|
|
|
placeholder="输入代码,每行一个或逗号分隔"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" @click="handleDetect" :loading="detecting">
|
|
|
|
|
|
<el-icon><Search /></el-icon> 检测缺失数据
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button type="success" @click="handleCache" :loading="caching" :disabled="!hasMissing">
|
|
|
|
|
|
<el-icon><Download /></el-icon> 一键缓存
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<el-card class="result-card" v-if="detectResult.length > 0">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<span>检测结果</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<el-table :data="detectResult" stripe>
|
|
|
|
|
|
<el-table-column prop="code" label="代码" width="120" />
|
|
|
|
|
|
<el-table-column prop="missingCount" label="缺失天数" width="100" />
|
|
|
|
|
|
<el-table-column label="缺失率">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-progress
|
|
|
|
|
|
:percentage="Math.round(row.missingRatio * 100)"
|
|
|
|
|
|
:status="row.missingRatio > 0.5 ? 'exception' : 'warning'"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="100">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-button type="primary" size="small" @click="showDetail(row)">
|
|
|
|
|
|
详情
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, reactive, computed } from 'vue'
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
import { detectMissingData, batchCacheData } from '@/api/cache'
|
|
|
|
|
|
|
|
|
|
|
|
const detecting = ref(false)
|
|
|
|
|
|
const caching = ref(false)
|
|
|
|
|
|
const codeInput = ref('000001.SZ\n600000.SH')
|
|
|
|
|
|
const detectResult = ref<any[]>([])
|
|
|
|
|
|
const taskId = ref<number | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const hasMissing = computed(() => detectResult.value.some(r => r.missingCount > 0))
|
|
|
|
|
|
|
|
|
|
|
|
const form = reactive({
|
|
|
|
|
|
securityType: 'stock',
|
|
|
|
|
|
periodType: 'daily',
|
|
|
|
|
|
startDate: getDefaultStartDate(),
|
|
|
|
|
|
endDate: getDefaultEndDate()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
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, '')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const parseCodes = () => {
|
|
|
|
|
|
return codeInput.value
|
|
|
|
|
|
.split(/[\n,,]/)
|
|
|
|
|
|
.map(c => c.trim())
|
|
|
|
|
|
.filter(c => c.length > 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleDetect = async () => {
|
|
|
|
|
|
const codes = parseCodes()
|
|
|
|
|
|
if (codes.length === 0) {
|
|
|
|
|
|
ElMessage.warning('请输入代码')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
detecting.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res: any = await detectMissingData({
|
|
|
|
|
|
security_type: form.securityType,
|
|
|
|
|
|
period_type: form.periodType,
|
|
|
|
|
|
start_date: form.startDate,
|
|
|
|
|
|
end_date: form.endDate,
|
|
|
|
|
|
code_list: codes
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
|
taskId.value = res.data.task_id
|
|
|
|
|
|
detectResult.value = res.data.missing_codes.map((item: any) => ({
|
|
|
|
|
|
code: item.code,
|
|
|
|
|
|
missingCount: item.missing_dates.length,
|
|
|
|
|
|
missingRatio: item.missing_dates.length > 0
|
|
|
|
|
|
? item.missing_dates.reduce((sum: number, d: any) => sum + d.missing_ratio, 0) / item.missing_dates.length
|
|
|
|
|
|
: 0,
|
|
|
|
|
|
details: item.missing_dates
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
detecting.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCache = async () => {
|
|
|
|
|
|
const codes = parseCodes()
|
|
|
|
|
|
if (codes.length === 0) return
|
|
|
|
|
|
|
|
|
|
|
|
caching.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
await batchCacheData({
|
|
|
|
|
|
security_type: form.securityType,
|
|
|
|
|
|
period_type: form.periodType,
|
|
|
|
|
|
start_date: form.startDate,
|
|
|
|
|
|
end_date: form.endDate,
|
|
|
|
|
|
code_list: codes
|
|
|
|
|
|
})
|
|
|
|
|
|
ElMessage.success('缓存任务已启动')
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
caching.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const showDetail = (row: any) => {
|
|
|
|
|
|
// 显示缺失详情
|
|
|
|
|
|
console.log(row.details)
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.detect-missing {
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.result-card {
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|