|
|
|
|
@ -204,7 +204,7 @@
|
|
|
|
|
<template #header>
|
|
|
|
|
<span>缺失代码列表(前100个)</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table :data="detectResult.missing_codes" stripe height="300">
|
|
|
|
|
<el-table :data="detectResult.missing_codes" stripe height="300" @row-click="handleRowClick">
|
|
|
|
|
<el-table-column prop="code" label="代码" width="120" />
|
|
|
|
|
<el-table-column prop="expected_count" label="预期天数" width="100" />
|
|
|
|
|
<el-table-column prop="actual_count" label="实际天数" width="100" />
|
|
|
|
|
@ -221,9 +221,79 @@
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="120">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-button type="primary" size="small" @click.stop="handleRowClick(row)">
|
|
|
|
|
详情
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 缺失交易日详情弹窗 -->
|
|
|
|
|
<el-dialog
|
|
|
|
|
v-model="detailDialogVisible"
|
|
|
|
|
:title="`缺失交易日详情 - ${selectedCode?.code || ''}`"
|
|
|
|
|
width="800px"
|
|
|
|
|
destroy-on-close
|
|
|
|
|
>
|
|
|
|
|
<div v-if="loadingDetail" style="text-align: center; padding: 20px;">
|
|
|
|
|
<el-icon class="is-loading" :size="40"><Loading /></el-icon>
|
|
|
|
|
<p>正在加载缺失交易日数据...</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else-if="missingDatesDetail">
|
|
|
|
|
<el-descriptions :column="4" border style="margin-bottom: 20px;">
|
|
|
|
|
<el-descriptions-item label="代码">{{ missingDatesDetail.code }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="预期天数">{{ missingDatesDetail.expected_count }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="实际天数">{{ missingDatesDetail.actual_count }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="缺失天数">
|
|
|
|
|
<el-tag type="warning">{{ missingDatesDetail.missing_count }}</el-tag>
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
<div style="margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center;">
|
|
|
|
|
<span>缺失交易日列表:</span>
|
|
|
|
|
<el-button type="success" @click="handleFillAllForCode" :loading="fillingAllForCode">
|
|
|
|
|
<el-icon><Download /></el-icon> 补齐所有缺失数据
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<el-table :data="missingDatesDetail.missing_dates" stripe height="400" v-loading="fillingSingleDate">
|
|
|
|
|
<el-table-column prop="date" label="交易日" width="120" />
|
|
|
|
|
<el-table-column label="状态" width="100">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="row.filling ? 'warning' : row.filled ? 'success' : 'info'" size="small">
|
|
|
|
|
{{ row.filling ? '补齐中' : row.filled ? '已补齐' : '待补齐' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="120">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
size="small"
|
|
|
|
|
@click="handleFillSingleDate(row)"
|
|
|
|
|
:loading="row.filling"
|
|
|
|
|
:disabled="row.filled"
|
|
|
|
|
>
|
|
|
|
|
补齐
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else style="text-align: center; padding: 20px; color: #999;">
|
|
|
|
|
<p>暂无数据</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
<!-- 批量检测结果 -->
|
|
|
|
|
<el-card class="result-card" v-if="batchDetectResult.length > 0">
|
|
|
|
|
<template #header>
|
|
|
|
|
@ -255,7 +325,8 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, reactive, computed } from 'vue'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import { detectMissingData, batchCacheData, detectAllMissingData, cacheAllMissingData, getCacheTask, fillMissingData } from '@/api/cache'
|
|
|
|
|
import { Loading } from '@element-plus/icons-vue'
|
|
|
|
|
import { detectMissingData, batchCacheData, detectAllMissingData, cacheAllMissingData, getCacheTask, fillMissingData, getMissingDatesForCode, fillSingleDateData, fillAllDatesForCode } from '@/api/cache'
|
|
|
|
|
|
|
|
|
|
const detecting = ref(false)
|
|
|
|
|
const caching = ref(false)
|
|
|
|
|
@ -276,6 +347,13 @@ const wsProgress = reactive({
|
|
|
|
|
complete: 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const detailDialogVisible = ref(false)
|
|
|
|
|
const selectedCode = ref<any>(null)
|
|
|
|
|
const loadingDetail = ref(false)
|
|
|
|
|
const missingDatesDetail = ref<any>(null)
|
|
|
|
|
const fillingSingleDate = ref(false)
|
|
|
|
|
const fillingAllForCode = ref(false)
|
|
|
|
|
|
|
|
|
|
let ws: WebSocket | null = null
|
|
|
|
|
|
|
|
|
|
const hasMissing = computed(() => batchDetectResult.value.some(r => r.missingCount > 0))
|
|
|
|
|
@ -594,6 +672,116 @@ const handleCache = async () => {
|
|
|
|
|
const showDetail = (row: any) => {
|
|
|
|
|
console.log(row.details)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleRowClick = async (row: any) => {
|
|
|
|
|
selectedCode.value = row
|
|
|
|
|
detailDialogVisible.value = true
|
|
|
|
|
loadingDetail.value = true
|
|
|
|
|
missingDatesDetail.value = null
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res: any = await getMissingDatesForCode(row.code, {
|
|
|
|
|
security_type: form.securityType,
|
|
|
|
|
period_type: form.periodType,
|
|
|
|
|
start_date: form.startDate,
|
|
|
|
|
end_date: form.endDate
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
missingDatesDetail.value = {
|
|
|
|
|
...res.data,
|
|
|
|
|
missing_dates: res.data.missing_dates.map((d: any) => ({
|
|
|
|
|
...d,
|
|
|
|
|
filling: false,
|
|
|
|
|
filled: false
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(res.message || '获取缺失交易日失败')
|
|
|
|
|
}
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
const errorMsg = error.response?.data?.message || error.message || '网络请求失败'
|
|
|
|
|
ElMessage.error(`获取缺失交易日失败: ${errorMsg}`)
|
|
|
|
|
} finally {
|
|
|
|
|
loadingDetail.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleFillSingleDate = async (row: any) => {
|
|
|
|
|
row.filling = true
|
|
|
|
|
fillingSingleDate.value = true
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res: any = await fillSingleDateData({
|
|
|
|
|
code: missingDatesDetail.value.code,
|
|
|
|
|
trade_date: row.date,
|
|
|
|
|
security_type: form.securityType,
|
|
|
|
|
period_type: form.periodType
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
row.filled = true
|
|
|
|
|
ElMessage.success(`${row.date} 补齐任务已启动`)
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(res.message || '补齐失败')
|
|
|
|
|
}
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
const errorMsg = error.response?.data?.message || error.message || '网络请求失败'
|
|
|
|
|
ElMessage.error(`补齐失败: ${errorMsg}`)
|
|
|
|
|
} finally {
|
|
|
|
|
row.filling = false
|
|
|
|
|
fillingSingleDate.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleFillAllForCode = async () => {
|
|
|
|
|
if (!missingDatesDetail.value || missingDatesDetail.value.missing_dates.length === 0) {
|
|
|
|
|
ElMessage.warning('没有缺失数据需要补齐')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fillingAllForCode.value = true
|
|
|
|
|
|
|
|
|
|
const missingDates = missingDatesDetail.value.missing_dates
|
|
|
|
|
.filter((d: any) => !d.filled)
|
|
|
|
|
.map((d: any) => d.date)
|
|
|
|
|
|
|
|
|
|
if (missingDates.length === 0) {
|
|
|
|
|
ElMessage.info('所有缺失数据已补齐')
|
|
|
|
|
fillingAllForCode.value = false
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res: any = await fillAllDatesForCode({
|
|
|
|
|
code: missingDatesDetail.value.code,
|
|
|
|
|
security_type: form.securityType,
|
|
|
|
|
period_type: form.periodType,
|
|
|
|
|
start_date: form.startDate,
|
|
|
|
|
end_date: form.endDate,
|
|
|
|
|
missing_dates: missingDates
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
ElMessage.success(`${missingDatesDetail.value.code} 补齐任务已启动,共 ${res.data.missing_count} 个交易日`)
|
|
|
|
|
missingDatesDetail.value.missing_dates.forEach((d: any) => {
|
|
|
|
|
if (!d.filled) {
|
|
|
|
|
d.filled = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(res.message || '补齐失败')
|
|
|
|
|
}
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
const errorMsg = error.response?.data?.message || error.message || '网络请求失败'
|
|
|
|
|
ElMessage.error(`补齐失败: ${errorMsg}`)
|
|
|
|
|
} finally {
|
|
|
|
|
fillingAllForCode.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|