fix(FEAT-MO-001): 修复TDX数据完整性与状态展示
This commit is contained in:
@@ -107,7 +107,7 @@ export interface components {
|
||||
BreadthDataDto: {
|
||||
advancing: number;
|
||||
declining: number;
|
||||
unchanged?: number | null;
|
||||
unchanged: number | null;
|
||||
};
|
||||
BreadthResponseDto: {
|
||||
/** Format: date-time */
|
||||
@@ -128,7 +128,12 @@ export interface components {
|
||||
FundDataDto: {
|
||||
changePercent: number;
|
||||
currentAmount: number;
|
||||
marketCount: number;
|
||||
previousAmount: number;
|
||||
/** @example 20260817 */
|
||||
previousTradeDate: string;
|
||||
/** @example 20260818 */
|
||||
tradeDate: string;
|
||||
/** @example 亿元 */
|
||||
unit: string;
|
||||
};
|
||||
@@ -184,7 +189,7 @@ export interface components {
|
||||
items: components['schemas']['SectorQuoteDto'][];
|
||||
};
|
||||
SectorQuoteDto: {
|
||||
changePercent?: number | null;
|
||||
changePercent: number;
|
||||
code: string;
|
||||
/** @enum {string} */
|
||||
direction: 'up' | 'down' | 'flat';
|
||||
|
||||
@@ -297,6 +297,18 @@ textarea {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.overview-panel .module-warning {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 7px 9px;
|
||||
border-left: 3px solid #b67a2d;
|
||||
background: #fff8ec;
|
||||
}
|
||||
|
||||
.overview-panel .module-warning strong {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.overview-grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
@@ -497,6 +509,10 @@ textarea {
|
||||
color: #19846e;
|
||||
}
|
||||
|
||||
.turnover-trend [data-direction='flat'] {
|
||||
color: #6f7a84;
|
||||
}
|
||||
|
||||
.turnover-trend {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import type { FundsResponse } from '@/types/market-overview';
|
||||
|
||||
defineProps<{ response: FundsResponse | null; loading: boolean }>();
|
||||
|
||||
function formatTradeDate(value: string | undefined): string {
|
||||
return value?.length === 8 ? `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6)}` : '—';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -11,25 +15,39 @@ defineProps<{ response: FundsResponse | null; loading: boolean }>();
|
||||
<span class="section-kicker">量能观察</span>
|
||||
<h2 id="funds-title">成交额趋势</h2>
|
||||
</div>
|
||||
<span class="panel-meta">TDX · 亿元</span>
|
||||
<span class="panel-meta">{{ formatTradeDate(response?.data?.tradeDate) }}</span>
|
||||
</div>
|
||||
<div v-if="loading" class="module-placeholder">正在读取资金趋势…</div>
|
||||
<div v-else-if="response?.status === 'unavailable'" class="module-unavailable">
|
||||
<strong>暂不可用</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<div v-else-if="response?.data" class="turnover-trend">
|
||||
<div>
|
||||
<span>三市成交额</span>
|
||||
<strong>{{ response.data.currentAmount.toLocaleString('zh-CN') }}</strong>
|
||||
<small>{{ response.data.unit }}</small>
|
||||
<template v-else-if="response?.data">
|
||||
<div v-if="response.status !== 'fresh'" class="module-warning">
|
||||
<strong>{{ response.status === 'partial' ? '部分数据' : '数据延迟' }}</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<p :data-direction="response.data.changePercent >= 0 ? 'up' : 'down'">
|
||||
较前一交易日
|
||||
{{ response.data.changePercent > 0 ? '+' : ''
|
||||
}}{{ response.data.changePercent.toFixed(2) }}%
|
||||
</p>
|
||||
</div>
|
||||
<div class="turnover-trend">
|
||||
<div>
|
||||
<span>{{ response.data.marketCount }} 市成交额</span>
|
||||
<strong>{{ response.data.currentAmount.toLocaleString('zh-CN') }}</strong>
|
||||
<small>{{ response.data.unit }}</small>
|
||||
</div>
|
||||
<p
|
||||
:data-direction="
|
||||
response.data.changePercent === 0
|
||||
? 'flat'
|
||||
: response.data.changePercent > 0
|
||||
? 'up'
|
||||
: 'down'
|
||||
"
|
||||
>
|
||||
较前一交易日
|
||||
{{ response.data.changePercent > 0 ? '+' : ''
|
||||
}}{{ response.data.changePercent.toFixed(2) }}%
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="module-placeholder">暂无成交额趋势数据</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -18,17 +18,23 @@ defineProps<{ response: BreadthResponse | null; loading: boolean }>();
|
||||
<strong>暂不可用</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<div v-else-if="response?.data" class="breadth-values">
|
||||
<div>
|
||||
<span>上涨</span><strong class="positive">{{ response.data.advancing }}</strong>
|
||||
<template v-else-if="response?.data">
|
||||
<div v-if="response.status !== 'fresh'" class="module-warning">
|
||||
<strong>{{ response.status === 'partial' ? '部分数据' : '数据延迟' }}</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>下跌</span><strong class="negative">{{ response.data.declining }}</strong>
|
||||
<div class="breadth-values">
|
||||
<div>
|
||||
<span>上涨</span><strong class="positive">{{ response.data.advancing }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>下跌</span><strong class="negative">{{ response.data.declining }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>平盘</span><strong>{{ response.data.unchanged ?? '—' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>平盘</span><strong>{{ response.data.unchanged ?? '—' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="module-placeholder">暂无宽度数据</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -54,6 +54,52 @@ describe('市场概览真实数据面板', () => {
|
||||
expect(wrapper.emitted('select')).toEqual([['881319.SH']]);
|
||||
});
|
||||
|
||||
it('市场宽度 partial 状态显示警告且保留有效数据', () => {
|
||||
const wrapper = mount(MarketBreadth, {
|
||||
props: {
|
||||
loading: false,
|
||||
response: {
|
||||
...meta,
|
||||
status: 'partial',
|
||||
warnings: ['部分市场涨跌家数不可用:北证市场'],
|
||||
data: { advancing: 10, declining: 5, unchanged: null },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('部分数据');
|
||||
expect(wrapper.text()).toContain('北证市场');
|
||||
expect(wrapper.text()).toContain('10');
|
||||
});
|
||||
|
||||
it('板块 delayed 状态显示警告和真实交易日期', () => {
|
||||
const wrapper = mount(SectorOverview, {
|
||||
props: {
|
||||
loading: false,
|
||||
response: {
|
||||
...meta,
|
||||
status: 'delayed',
|
||||
warnings: ['板块数据已延迟'],
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
code: '881319.SH',
|
||||
name: '半导体',
|
||||
changePercent: 2.56,
|
||||
direction: 'up',
|
||||
tradeDate: '20260818',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('数据延迟');
|
||||
expect(wrapper.text()).toContain('板块数据已延迟');
|
||||
expect(wrapper.text()).toContain('2026-08-18');
|
||||
});
|
||||
|
||||
it('资金模块明确展示成交额和与上一交易日的变化', () => {
|
||||
const wrapper = mount(FundTrend, {
|
||||
props: {
|
||||
@@ -65,6 +111,9 @@ describe('市场概览真实数据面板', () => {
|
||||
previousAmount: 11000,
|
||||
changePercent: 12.23,
|
||||
unit: '亿元' as const,
|
||||
marketCount: 3,
|
||||
tradeDate: '20260818',
|
||||
previousTradeDate: '20260817',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -75,4 +124,32 @@ describe('市场概览真实数据面板', () => {
|
||||
expect(wrapper.text()).toContain('较前一交易日 +12.23%');
|
||||
expect(wrapper.text()).not.toContain('主力净流入');
|
||||
});
|
||||
|
||||
it('部分数据保留数值并显示警告和实际交易日期', () => {
|
||||
const wrapper = mount(FundTrend, {
|
||||
props: {
|
||||
loading: false,
|
||||
response: {
|
||||
...meta,
|
||||
status: 'partial',
|
||||
warnings: ['部分市场成交额不可用,当前汇总 2 个市场'],
|
||||
data: {
|
||||
currentAmount: 100,
|
||||
previousAmount: 100,
|
||||
changePercent: 0,
|
||||
unit: '亿元' as const,
|
||||
marketCount: 2,
|
||||
tradeDate: '20260818',
|
||||
previousTradeDate: '20260817',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain('部分数据');
|
||||
expect(wrapper.text()).toContain('部分市场成交额不可用');
|
||||
expect(wrapper.text()).toContain('2 市成交额');
|
||||
expect(wrapper.text()).toContain('2026-08-18');
|
||||
expect(wrapper.get('[data-direction="flat"]').attributes('data-direction')).toBe('flat');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,10 @@ import type { SectorsResponse } from '@/types/market-overview';
|
||||
|
||||
defineProps<{ response: SectorsResponse | null; loading: boolean }>();
|
||||
const emit = defineEmits<{ select: [sector: string] }>();
|
||||
|
||||
function formatTradeDate(value: string | undefined): string {
|
||||
return value?.length === 8 ? `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6)}` : '—';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -12,32 +16,34 @@ const emit = defineEmits<{ select: [sector: string] }>();
|
||||
<span class="section-kicker">板块扫描</span>
|
||||
<h2 id="sectors-title">强弱分布</h2>
|
||||
</div>
|
||||
<span class="panel-meta">点击查看详情</span>
|
||||
<span class="panel-meta">{{ formatTradeDate(response?.data?.items[0]?.tradeDate) }}</span>
|
||||
</div>
|
||||
<div v-if="loading" class="module-placeholder">正在读取板块…</div>
|
||||
<div v-else-if="response?.status === 'unavailable'" class="module-unavailable">
|
||||
<strong>暂不可用</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<div v-else-if="response?.data?.items.length" class="sector-list">
|
||||
<button
|
||||
v-for="sector in response.data.items"
|
||||
:key="sector.code"
|
||||
type="button"
|
||||
:data-direction="sector.direction"
|
||||
:title="`进入${sector.name}详细分析`"
|
||||
@click="emit('select', sector.code)"
|
||||
>
|
||||
<span>{{ sector.name }}</span>
|
||||
<strong>
|
||||
{{
|
||||
sector.changePercent == null
|
||||
? '—'
|
||||
: `${sector.changePercent > 0 ? '+' : ''}${sector.changePercent.toFixed(2)}%`
|
||||
}}
|
||||
</strong>
|
||||
</button>
|
||||
</div>
|
||||
<template v-else-if="response?.data?.items.length">
|
||||
<div v-if="response.status !== 'fresh'" class="module-warning">
|
||||
<strong>{{ response.status === 'partial' ? '部分数据' : '数据延迟' }}</strong>
|
||||
<span>{{ response.warnings[0] }}</span>
|
||||
</div>
|
||||
<div class="sector-list">
|
||||
<button
|
||||
v-for="sector in response.data.items"
|
||||
:key="sector.code"
|
||||
type="button"
|
||||
:data-direction="sector.direction"
|
||||
:title="`进入${sector.name}详细分析`"
|
||||
@click="emit('select', sector.code)"
|
||||
>
|
||||
<span>{{ sector.name }}</span>
|
||||
<strong>{{
|
||||
`${sector.changePercent > 0 ? '+' : ''}${sector.changePercent.toFixed(2)}%`
|
||||
}}</strong>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="module-placeholder">暂无板块数据</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user