fix market ranking state and quote rendering

This commit is contained in:
clz
2026-07-27 15:19:28 +08:00
parent c05fc03fd8
commit 30d9154086
4 changed files with 126 additions and 68 deletions
@@ -0,0 +1,24 @@
import {
CardData,
RankItem,
RecommendFuturesItem,
} from './MarketRankingModels';
export function cardDataToContracts(cardData: CardData): RecommendFuturesItem[] {
return cardData.tableList.map((item: RankItem): RecommendFuturesItem => ({
contract_code: item.code,
contract_name: item.name,
market: item.market,
}));
}
export function contractsToPlaceholderCardData(contracts: RecommendFuturesItem[]): CardData {
const cardData: CardData = {
tableList: contracts.map((contract: RecommendFuturesItem): RankItem => ({
code: contract.contract_code,
market: contract.market,
name: contract.contract_name,
})),
};
return cardData;
}
@@ -6,17 +6,23 @@ import {
RankItem, RecommendFuturesItem, TabMetric,
} from './MarketRankingModels';
import { MarketRankingDataFetcher } from './MarketRankingDataFetcher';
import {
cardDataToContracts,
contractsToPlaceholderCardData,
} from './MarketRankingDataMapper';
import { MarketRankingHqRequestClient } from '../node/clients/MarketRankingHqRequestClient';
import { formatGreatNumber, formatPercent, formatPrice, riseFallColor } from '../common/NumberFormat';
import { AllCardsCard } from '../node/model/AiDiagnosisNodeModel';
import { PERIOD_RANGES, TAB_METRICS } from './MarketRankingConstant';
import { enableDarkMode } from '@kernel/theme_manager';
import { EmitterConstants, GlobalContext, HXLog, jumpToQuote } from 'biz_common';
import { QuoteCustomSettingManager, StandardPriceType } from 'biz_quote';
import { EmitterConstants, GlobalContext, jumpToQuote } from 'biz_common';
import { RouterUtil } from '../util/RouterUtil';
const NORMAL_SIZE = 3;
const MAX_SIZE = 5;
const UNFOLDED_STORAGE_KEY = 'firstPageMarketRankingUnfolded';
PersistentStorage.persistProp<boolean>(UNFOLDED_STORAGE_KEY, false);
@Component
export struct MarketRankingNodeComponent {
@@ -29,13 +35,14 @@ export struct MarketRankingNodeComponent {
@State cardConfig: AllCardsCard | undefined = undefined;
@State cardData: CardData = { tableList: [] };
private contracts: RecommendFuturesItem[] = [];
private tabDataCache: Map<string, CardData> = new Map<string, CardData>();
// 区分加载中和暂无数据,与 HotListNodeComponent 的 dataReady 约定保持一致
@State dataReady: boolean = false;
// Tab 与展开状态
@State primaryIdx: number = 0;
@State secondaryIdx: number = 0;
@State unfolded: boolean = false;
@StorageLink(UNFOLDED_STORAGE_KEY) unfolded: boolean = false;
// Tab 滚动区域状态
@State tabViewportWidth: number = 0;
@@ -53,8 +60,6 @@ export struct MarketRankingNodeComponent {
private hqUnsubscribe: (() => void) | undefined;
private hqSubscriptionVersion: number = 0;
private isTcpEventSubscribed: boolean = false;
private lastStandardPriceType: StandardPriceType =
QuoteCustomSettingManager.getInstance().getStandardPriceType();
private tcpReconnectListener = (): void => {
if (!this.canUseHq()) {
return;
@@ -114,19 +119,9 @@ export struct MarketRankingNodeComponent {
return;
}
this.subscribeTcpNetStatus();
this.syncStandardPriceType();
this.restartCurrentSubscription();
}
private syncStandardPriceType(): void {
const currentStandardPriceType: StandardPriceType =
QuoteCustomSettingManager.getInstance().getStandardPriceType();
if (this.lastStandardPriceType !== currentStandardPriceType) {
HXLog.d('MarketRankingNodeComponent', 'Standard price type changed, resubscribe');
this.lastStandardPriceType = currentStandardPriceType;
}
}
private subscribeTcpNetStatus(): void {
if (this.isTcpEventSubscribed || !this.canUseHq()) {
return;
@@ -185,11 +180,21 @@ export struct MarketRankingNodeComponent {
}
const requestVersion: number = this.dataRequestVersion + 1;
this.dataRequestVersion = requestVersion;
// 切换 Tab 后立即清理旧列表,避免新请求返回前闪现上一 Tab 的数据。
const cacheKey: string = this.getTabCacheKey(primaryIdx, secondaryIdx);
const cachedData: CardData | undefined = this.tabDataCache.get(cacheKey);
this.stopSubscribeHq();
this.contracts = [];
this.cardData = { tableList: [] };
this.dataReady = false;
if (cachedData !== undefined) {
this.cardData = cachedData;
this.contracts = cardDataToContracts(cachedData);
this.dataReady = true;
if (this.firstPageVisible) {
this.subscribeHq(primaryIdx, secondaryIdx);
}
} else {
this.contracts = [];
this.cardData = { tableList: [] };
this.dataReady = false;
}
try {
const contracts = await MarketRankingDataFetcher
.getInstance()
@@ -198,14 +203,11 @@ export struct MarketRankingNodeComponent {
return;
}
this.contracts = contracts;
this.cardData = {
tableList: contracts.map((contract: RecommendFuturesItem): RankItem => ({
code: contract.contract_code,
market: contract.market,
name: contract.contract_name,
})),
};
if (cachedData === undefined) {
this.cardData = contractsToPlaceholderCardData(contracts);
}
this.dataReady = true;
this.tabDataCache.set(cacheKey, this.cardData);
if (this.firstPageVisible) {
this.subscribeHq(primaryIdx, secondaryIdx);
}
@@ -213,8 +215,10 @@ export struct MarketRankingNodeComponent {
if (!this.isCurrentDataRequest(requestVersion, primaryIdx, secondaryIdx)) {
return;
}
this.contracts = [];
this.cardData = { tableList: [] };
if (cachedData === undefined) {
this.contracts = [];
this.cardData = { tableList: [] };
}
this.dataReady = true;
}
}
@@ -235,6 +239,7 @@ export struct MarketRankingNodeComponent {
primaryIdx === this.primaryIdx &&
secondaryIdx === this.secondaryIdx) {
this.cardData = cardData;
this.tabDataCache.set(this.getTabCacheKey(primaryIdx, secondaryIdx), cardData);
}
});
}
@@ -266,6 +271,14 @@ export struct MarketRankingNodeComponent {
secondaryIdx === this.secondaryIdx;
}
private getTabCacheKey(primaryIdx: number, secondaryIdx: number): string {
const metric: TabMetric = TAB_METRICS[primaryIdx];
if (metric.hasChildren) {
return `${metric.id}_${PERIOD_RANGES[secondaryIdx].id}`;
}
return `${metric.id}`;
}
// ==================== 页面跳转 ====================
// 对齐 futures-homepage jumpToFenShi,跳转客户端行情分时页。
@@ -357,26 +370,24 @@ export struct MarketRankingNodeComponent {
})
// 展开/收起按钮,样式对齐 HotListNodeComponent 的展开/收起按钮
if (this.cardData.tableList.length > NORMAL_SIZE) {
Row() {
Text(this.unfolded
? $r('app.string.hotlist_collapse')
: $r('app.string.hotlist_expand'))
.fontColor($r('app.color.elements_text_tertiary'))
.fontSize(12)
.height(16)
.width('100%')
.textAlign(TextAlign.Center)
}
.width(40)
.height(20)
.borderRadius(10)
.backgroundColor($r('app.color.elements_button_bg_disable_02'))
.margin({ left: 8 })
.onClick(() => {
this.unfolded = !this.unfolded;
})
Row() {
Text(this.unfolded
? $r('app.string.hotlist_collapse')
: $r('app.string.hotlist_expand'))
.fontColor($r('app.color.elements_text_tertiary'))
.fontSize(12)
.height(16)
.width('100%')
.textAlign(TextAlign.Center)
}
.width(40)
.height(20)
.borderRadius(10)
.backgroundColor($r('app.color.elements_button_bg_disable_02'))
.margin({ left: 8 })
.onClick(() => {
this.toggleUnfolded();
})
Blank()
}
@@ -540,7 +551,6 @@ export struct MarketRankingNodeComponent {
}
.width('100%')
.height(this.emptyHeight())
.margin({ top: 10 })
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
}
@@ -581,6 +591,8 @@ export struct MarketRankingNodeComponent {
)
}
.width('100%')
.height(this.emptyHeight())
.animation({ duration: 180, curve: Curve.EaseInOut })
}
@Builder
@@ -626,6 +638,10 @@ export struct MarketRankingNodeComponent {
// ==================== 展示计算 ====================
private toggleUnfolded(): void {
this.unfolded = !this.unfolded;
}
private showTabGradient(): boolean {
const maxOffset = this.tabContentWidth - this.tabViewportWidth;
return maxOffset > 0 && this.tabScrollOffset < maxOffset;
@@ -1,7 +1,12 @@
import { AbsRowData, RowData, TableData } from '@b2b/hq_table';
import { TableRequestClient } from '@b2c/lib_baseui';
import { TableConstants as BaseTableConstants, TableRequestClient } from '@b2c/lib_baseui';
import { RequestHelper, StuffTableStruct } from '@kernel/lib_communication';
import { applyStandardPriceToTableData } from 'biz_quote';
import {
QuoteCustomSettingManager,
StandardPriceType,
StandardPriceTypeHelper,
applyStandardPriceToTableData,
} from 'biz_quote';
import {
CardData,
RankItem,
@@ -16,6 +21,17 @@ import { TableConstants } from '../../market-ranking/TableConstants';
const FRAME_ID = 2201;
const PAGE_ID = 4106;
const REQUIRED_IDS_FOR_STANDARD_PRICE: number[] = [
BaseTableConstants.DATA_ID_CODE_4,
BaseTableConstants.DATA_ID_PRICE,
BaseTableConstants.DATA_ID_ZF,
BaseTableConstants.DATA_ID_ZD,
BaseTableConstants.DATA_ID_MARKET,
BaseTableConstants.DATA_ID_MARKET_OLD,
StandardPriceTypeHelper.FIELD_ZUO_SHOU,
StandardPriceTypeHelper.FIELD_JIN_KAI,
StandardPriceTypeHelper.FIELD_ZUO_JIE,
];
export type MarketRankingCardDataListener = (cardData: CardData) => void;
@@ -107,7 +123,9 @@ class MarketRankingTableRequestClient extends TableRequestClient {
return value;
}
if (typeof value === 'string' && value !== '' && value !== '--') {
const result: number = Number(value);
// 行情解析及标准价换算后的字段可能带 "%" 等展示后缀。
// 与仓库内其他行情卡片保持一致,读取开头的有效数值。
const result: number = parseFloat(value);
return Number.isNaN(result) ? undefined : result;
}
return undefined;
@@ -142,14 +160,12 @@ export class MarketRankingHqRequestClient {
const sortId: number = this.getSortId(primaryIdx, secondaryIdx);
const fieldIds: number[] = [
TableConstants.DATA_ID_NAME,
TableConstants.DATA_ID_PRICE,
TableConstants.DATA_ID_CODE_4,
TableConstants.DATA_ID_MARKET,
TableConstants.DATA_ID_ZF,
TableConstants.DATA_ID_PRE_PRICE,
TableConstants.DATA_ID_OPEN_PRICE,
TableConstants.DATA_ID_JSJ,
];
REQUIRED_IDS_FOR_STANDARD_PRICE.forEach((fieldId: number): void => {
if (!fieldIds.includes(fieldId)) {
fieldIds.push(fieldId);
}
});
if (!fieldIds.includes(sortId)) {
fieldIds.push(sortId);
}
@@ -182,11 +198,15 @@ export class MarketRankingHqRequestClient {
): string {
const metric: TabMetric = TAB_METRICS[primaryIdx];
const sortOrder: string = metric.sortOrder;
const computeMode: StandardPriceType =
QuoteCustomSettingManager.getInstance().getStandardPriceType();
return `\r\nsortid=${sortId}\r\n` +
`sortorder=${sortOrder}\r\n` +
`push=1\r\n` +
`dataitem=${fieldIds.map((fieldId: number): string => `${fieldId},`).join('')}\r\n` +
`codelist=${this.formatCodeList(contracts)}\r\n` +
`update=1\r\n` +
`computemode=${computeMode}\r\n` +
`scenario=qht_qihuo_sort\r\n` +
`precision=1\r\n` +
`pushtime=2.5\r\n`;