add AI pick card
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
export const MAX_TABS = 5;
|
||||||
|
export const MAX_ITEMS = 3;
|
||||||
|
export const MAX_TAGS = 3;
|
||||||
|
export const MAX_SHOW_KEYS = 3;
|
||||||
|
export const MAX_DOUBLE_KEYS = 2;
|
||||||
|
|
||||||
|
export const EMPTY_IMAGE_LIGHT =
|
||||||
|
'https://u.thsi.cn/imgsrc/bbs/8eee9cd4fa7e359032de11a3f7c2a70b_300_230.png';
|
||||||
|
export const EMPTY_IMAGE_DARK =
|
||||||
|
'https://u.thsi.cn/imgsrc/bbs/1c4e679244ed87231befad2b04cddfe2_300_230.png';
|
||||||
|
|
||||||
|
export const BLACK_LIST: string[] = [
|
||||||
|
'合约代码',
|
||||||
|
'合约简称',
|
||||||
|
'收盘价',
|
||||||
|
'最新价',
|
||||||
|
'涨跌幅',
|
||||||
|
'code',
|
||||||
|
'market_id',
|
||||||
|
'market_code',
|
||||||
|
'最新涨跌幅',
|
||||||
|
];
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import { http } from '@kit.NetworkKit';
|
||||||
|
import { GlobalContext, HXLog } from 'biz_common';
|
||||||
|
import { buildHeader } from '../util/Header';
|
||||||
|
import { FirstPageCardsConfigLoader } from '../node/datacenter/FirstPageCardsConfigLoader';
|
||||||
|
import { AllCardsCard, ModDataItem } from '../node/model/AiDiagnosisNodeModel';
|
||||||
|
import {
|
||||||
|
PickContractItem,
|
||||||
|
PickStrategy,
|
||||||
|
PickTabData,
|
||||||
|
RawPickResponse,
|
||||||
|
RawPickTabItem,
|
||||||
|
RawStrategyDetail,
|
||||||
|
RawStrategyItem,
|
||||||
|
} from './AiPickModels';
|
||||||
|
import { getResData, getTableList, ResData } from './AiPickUtils';
|
||||||
|
import { MAX_ITEMS, MAX_TABS, MAX_TAGS } from './AiPickConstant';
|
||||||
|
|
||||||
|
const EMPTY_RAW_STRATEGY_ITEM: RawStrategyItem = {};
|
||||||
|
const EMPTY_RAW_STRATEGY_DETAIL: RawStrategyDetail = {
|
||||||
|
id: '',
|
||||||
|
strategy_name: '',
|
||||||
|
strategy_type: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseRawTab(item: RawPickTabItem): PickTabData {
|
||||||
|
const isSystemPeriod: boolean = item.type === 'system_period';
|
||||||
|
const strategyItem: RawStrategyItem = (
|
||||||
|
isSystemPeriod ? item.system_strategy : item.custom_strategy
|
||||||
|
) ?? EMPTY_RAW_STRATEGY_ITEM;
|
||||||
|
const strategyDetail: RawStrategyDetail = (
|
||||||
|
isSystemPeriod ? strategyItem.system_strategy : strategyItem.custom_strategy
|
||||||
|
) ?? EMPTY_RAW_STRATEGY_DETAIL;
|
||||||
|
const resData: ResData = getResData(strategyItem);
|
||||||
|
const allItems: PickContractItem[] =
|
||||||
|
getTableList(resData.list, resData.resultValueList);
|
||||||
|
const strategy: PickStrategy = {
|
||||||
|
id: strategyDetail.id,
|
||||||
|
name: strategyDetail.strategy_name,
|
||||||
|
detail: strategyDetail.user_question || strategyDetail.content || '',
|
||||||
|
type: strategyDetail.strategy_type || 'custom_strategy',
|
||||||
|
periodType: item.type,
|
||||||
|
tagList: (strategyDetail.tag_list ??
|
||||||
|
[GlobalContext.get().resourceManager
|
||||||
|
.getStringSync($r('app.string.ai_pick_custom_tag'))])
|
||||||
|
.slice(0, MAX_TAGS),
|
||||||
|
imgs: isSystemPeriod
|
||||||
|
? [strategyDetail.white_img ?? '', strategyDetail.black_img ?? '']
|
||||||
|
: [],
|
||||||
|
count: strategyItem.detail_count ?? 0,
|
||||||
|
};
|
||||||
|
const tabData: PickTabData = {
|
||||||
|
strategy: strategy,
|
||||||
|
items: allItems.slice(0, MAX_ITEMS),
|
||||||
|
keysList: resData.showKeyList,
|
||||||
|
fieldList: resData.fieldList,
|
||||||
|
};
|
||||||
|
return tabData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRawTabs(rawTabs: RawPickTabItem[]): PickTabData[] {
|
||||||
|
return rawTabs
|
||||||
|
.slice(0, MAX_TABS)
|
||||||
|
.map((item: RawPickTabItem): PickTabData => parseRawTab(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AiPickDataFetcher {
|
||||||
|
private static readonly instance: AiPickDataFetcher = new AiPickDataFetcher();
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInstance(): AiPickDataFetcher {
|
||||||
|
return AiPickDataFetcher.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchCardConfig(): AllCardsCard | undefined {
|
||||||
|
return FirstPageCardsConfigLoader.getConfig()?.aiPick;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchCardData(): Promise<PickTabData[]> {
|
||||||
|
const cardConfig: AllCardsCard | undefined = this.fetchCardConfig();
|
||||||
|
const modData: ModDataItem | undefined = cardConfig?.mod_data[0];
|
||||||
|
const configuredUrl: string = modData?.url ?? '';
|
||||||
|
if (configuredUrl === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestOptions: http.HttpRequestOptions = {
|
||||||
|
method: http.RequestMethod.GET,
|
||||||
|
header: buildHeader(),
|
||||||
|
connectTimeout: 60000,
|
||||||
|
readTimeout: 60000,
|
||||||
|
};
|
||||||
|
const httpRequest: http.HttpRequest = http.createHttp();
|
||||||
|
try {
|
||||||
|
const requestUrl: string = this.buildRequestUrl(configuredUrl, Date.now());
|
||||||
|
const response: http.HttpResponse =
|
||||||
|
await httpRequest.request(requestUrl, requestOptions);
|
||||||
|
if (response.responseCode !== http.ResponseCode.OK) {
|
||||||
|
throw new Error('request failed: ' + response.responseCode);
|
||||||
|
}
|
||||||
|
const responseText: string = response.result as string;
|
||||||
|
const result: RawPickResponse = JSON.parse(responseText) as RawPickResponse;
|
||||||
|
const responseCode: number = result.code ?? result.status_code ?? 0;
|
||||||
|
if (responseCode !== 0 || result.data === undefined) {
|
||||||
|
throw new Error('response code: ' + responseCode);
|
||||||
|
}
|
||||||
|
return parseRawTabs(result.data);
|
||||||
|
} catch (requestError) {
|
||||||
|
const errorInfo: Error = requestError as Error;
|
||||||
|
HXLog.e('AiPickDataFetcher', 'fetchCardData error: ' + errorInfo.message);
|
||||||
|
throw new Error(errorInfo.message);
|
||||||
|
} finally {
|
||||||
|
httpRequest.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildRequestUrl(configuredUrl: string, timestamp: number): string {
|
||||||
|
const separator: string = configuredUrl.includes('?') ? '&' : '?';
|
||||||
|
return `${configuredUrl}${separator}_=${timestamp}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
export interface PickContractItem {
|
||||||
|
contract: string;
|
||||||
|
market: string;
|
||||||
|
name: string;
|
||||||
|
price?: number;
|
||||||
|
priceChg?: number;
|
||||||
|
isMain: boolean;
|
||||||
|
extraFields: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PickFieldItem {
|
||||||
|
key: string;
|
||||||
|
unit: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PickStrategy {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
detail: string;
|
||||||
|
type: string;
|
||||||
|
periodType: string;
|
||||||
|
tagList: string[];
|
||||||
|
imgs: string[];
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PickTabData {
|
||||||
|
strategy: PickStrategy;
|
||||||
|
items: PickContractItem[];
|
||||||
|
keysList: string[];
|
||||||
|
fieldList: PickFieldItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CardData {
|
||||||
|
tabs: PickTabData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawStrategyDetail {
|
||||||
|
id: string;
|
||||||
|
strategy_name: string;
|
||||||
|
user_question?: string;
|
||||||
|
content?: string;
|
||||||
|
strategy_type: string;
|
||||||
|
tag_list?: string[];
|
||||||
|
white_img?: string;
|
||||||
|
black_img?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawFieldItem {
|
||||||
|
key: string;
|
||||||
|
type: string;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawDetailItem {
|
||||||
|
contract: string;
|
||||||
|
market: string;
|
||||||
|
contract_name: string;
|
||||||
|
main_contract_type: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RawResultItem = Record<string, number | string>;
|
||||||
|
|
||||||
|
export interface RawStrategyItem {
|
||||||
|
custom_strategy?: RawStrategyDetail;
|
||||||
|
system_strategy?: RawStrategyDetail;
|
||||||
|
detail_list?: RawDetailItem[];
|
||||||
|
field_list?: RawFieldItem[];
|
||||||
|
origin_result_list?: RawResultItem[];
|
||||||
|
detail_count?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawPickTabItem {
|
||||||
|
type: string;
|
||||||
|
system_strategy?: RawStrategyItem;
|
||||||
|
custom_strategy?: RawStrategyItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawPickResponse {
|
||||||
|
code?: number;
|
||||||
|
status_code?: number;
|
||||||
|
data?: RawPickTabItem[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,746 @@
|
|||||||
|
import { emitter } from '@kit.BasicServicesKit';
|
||||||
|
import { enableDarkMode } from '@kernel/theme_manager';
|
||||||
|
import { EmitterConstants, GlobalContext, jumpToQuote } from 'biz_common';
|
||||||
|
import { AiPickDataFetcher } from './AiPickDataFetcher';
|
||||||
|
import { AiPickHqRequestClient } from '../node/clients/AiPickHqRequestClient';
|
||||||
|
import {
|
||||||
|
CardData,
|
||||||
|
PickContractItem,
|
||||||
|
PickStrategy,
|
||||||
|
PickTabData,
|
||||||
|
} from './AiPickModels';
|
||||||
|
import {
|
||||||
|
cellColor,
|
||||||
|
cellValue,
|
||||||
|
formatColName,
|
||||||
|
getUniqueContracts,
|
||||||
|
} from './AiPickUtils';
|
||||||
|
import {
|
||||||
|
EMPTY_IMAGE_DARK,
|
||||||
|
EMPTY_IMAGE_LIGHT,
|
||||||
|
MAX_ITEMS,
|
||||||
|
} from './AiPickConstant';
|
||||||
|
import { AllCardsCard } from '../node/model/AiDiagnosisNodeModel';
|
||||||
|
import { RouterUtil } from '../util/RouterUtil';
|
||||||
|
import { getResourceString } from '../util/ResourceUtil';
|
||||||
|
|
||||||
|
interface StrategyDescParams {
|
||||||
|
tabIdx: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TAB_BAR_HEIGHT = 30;
|
||||||
|
const STRATEGY_DESC_HEIGHT = 86;
|
||||||
|
const TABLE_HEADER_HEIGHT = 40;
|
||||||
|
const TABLE_ROW_HEIGHT = 45;
|
||||||
|
const TABLE_HEIGHT = TABLE_HEADER_HEIGHT + TABLE_ROW_HEIGHT * MAX_ITEMS;
|
||||||
|
const CUSTOM_STRATEGY_HEIGHT = 30;
|
||||||
|
const MAIN_CONTENT_HEIGHT =
|
||||||
|
TAB_BAR_HEIGHT + STRATEGY_DESC_HEIGHT + TABLE_HEIGHT + CUSTOM_STRATEGY_HEIGHT;
|
||||||
|
const NO_CONTRACT_HEIGHT = STRATEGY_DESC_HEIGHT + TABLE_HEIGHT;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export struct AiPickNodeComponent {
|
||||||
|
@Link @Watch('onRefreshTriggerChange') refreshTrigger: number;
|
||||||
|
@Consume('firstPageVisible') @Watch('onFirstPageVisibleChange')
|
||||||
|
firstPageVisible: boolean = true;
|
||||||
|
@StorageProp(enableDarkMode) isDarkMode: boolean = false;
|
||||||
|
|
||||||
|
@State cardConfig: AllCardsCard | undefined = undefined;
|
||||||
|
@State cardData: CardData = { tabs: [] };
|
||||||
|
@State dataReady: boolean = false;
|
||||||
|
@State tabIdx: number = 0;
|
||||||
|
@State tabViewportWidth: number = 0;
|
||||||
|
@State tabContentWidth: number = 0;
|
||||||
|
@State tabScrollOffset: number = 0;
|
||||||
|
@State showExplainSheet: boolean = false;
|
||||||
|
|
||||||
|
private isComponentActive: boolean = false;
|
||||||
|
private dataRequestVersion: number = 0;
|
||||||
|
private hqSubscriptionVersion: number = 0;
|
||||||
|
private hqUnsubscribe: (() => void) | undefined;
|
||||||
|
private isTcpEventSubscribed: boolean = false;
|
||||||
|
private tcpReconnectListener = (): void => {
|
||||||
|
if (!this.canUseHq()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout((): void => {
|
||||||
|
if (this.canUseHq()) {
|
||||||
|
this.restartSubscription();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
aboutToAppear(): void {
|
||||||
|
this.isComponentActive = true;
|
||||||
|
this.updateCardConfig();
|
||||||
|
if (this.firstPageVisible) {
|
||||||
|
this.subscribeTcpNetStatus();
|
||||||
|
}
|
||||||
|
this.updateCardData();
|
||||||
|
}
|
||||||
|
|
||||||
|
aboutToDisappear(): void {
|
||||||
|
this.isComponentActive = false;
|
||||||
|
this.dataRequestVersion += 1;
|
||||||
|
this.stopSubscribeHq();
|
||||||
|
this.unSubscribeTcpNetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRefreshTriggerChange(): void {
|
||||||
|
if (!this.isComponentActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.updateCardConfig();
|
||||||
|
this.updateCardData();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFirstPageVisibleChange(): void {
|
||||||
|
if (!this.isComponentActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.firstPageVisible) {
|
||||||
|
this.stopSubscribeHq();
|
||||||
|
this.unSubscribeTcpNetStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.subscribeTcpNetStatus();
|
||||||
|
if (this.cardData.tabs.length > 0) {
|
||||||
|
this.subscribeHq();
|
||||||
|
} else {
|
||||||
|
this.updateCardData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateCardConfig(): void {
|
||||||
|
this.cardConfig = AiPickDataFetcher.getInstance().fetchCardConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateCardData(): Promise<void> {
|
||||||
|
if (!this.isComponentActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const requestVersion: number = this.dataRequestVersion + 1;
|
||||||
|
this.dataRequestVersion = requestVersion;
|
||||||
|
this.stopSubscribeHq();
|
||||||
|
if (this.cardData.tabs.length === 0) {
|
||||||
|
this.dataReady = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tabs: PickTabData[] =
|
||||||
|
await AiPickDataFetcher.getInstance().fetchCardData();
|
||||||
|
if (!this.isCurrentDataRequest(requestVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextTabs: PickTabData[] = this.mergeCachedQuotes(tabs);
|
||||||
|
if (this.tabIdx >= nextTabs.length) {
|
||||||
|
this.tabIdx = 0;
|
||||||
|
}
|
||||||
|
this.cardData = { tabs: nextTabs };
|
||||||
|
this.dataReady = true;
|
||||||
|
if (nextTabs.length > 0 && this.firstPageVisible) {
|
||||||
|
this.subscribeHq();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if (!this.isCurrentDataRequest(requestVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.dataReady = true;
|
||||||
|
if (this.cardData.tabs.length > 0 && this.firstPageVisible) {
|
||||||
|
this.subscribeHq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mergeCachedQuotes(tabs: PickTabData[]): PickTabData[] {
|
||||||
|
const cachedContracts: PickContractItem[] = getUniqueContracts(this.cardData);
|
||||||
|
const cachedMap: Map<string, PickContractItem> =
|
||||||
|
new Map<string, PickContractItem>();
|
||||||
|
cachedContracts.forEach((item: PickContractItem): void => {
|
||||||
|
cachedMap.set(`${item.contract}_${item.market}`, item);
|
||||||
|
});
|
||||||
|
return tabs.map((tab: PickTabData): PickTabData => {
|
||||||
|
const items: PickContractItem[] =
|
||||||
|
tab.items.map((item: PickContractItem): PickContractItem => {
|
||||||
|
const cachedItem: PickContractItem | undefined =
|
||||||
|
cachedMap.get(`${item.contract}_${item.market}`);
|
||||||
|
const nextItem: PickContractItem = {
|
||||||
|
contract: item.contract,
|
||||||
|
market: item.market,
|
||||||
|
name: item.name,
|
||||||
|
price: cachedItem?.price,
|
||||||
|
priceChg: cachedItem?.priceChg,
|
||||||
|
isMain: item.isMain,
|
||||||
|
extraFields: item.extraFields,
|
||||||
|
};
|
||||||
|
return nextItem;
|
||||||
|
});
|
||||||
|
const nextTab: PickTabData = {
|
||||||
|
strategy: tab.strategy,
|
||||||
|
items: items,
|
||||||
|
keysList: tab.keysList,
|
||||||
|
fieldList: tab.fieldList,
|
||||||
|
};
|
||||||
|
return nextTab;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private subscribeHq(): void {
|
||||||
|
this.stopSubscribeHq();
|
||||||
|
if (!this.canUseHq() || this.cardData.tabs.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const subscriptionVersion: number = this.hqSubscriptionVersion;
|
||||||
|
this.hqUnsubscribe = AiPickHqRequestClient.getInstance().subscribeHq(
|
||||||
|
this.cardData,
|
||||||
|
(cardData: CardData): void => {
|
||||||
|
if (this.canUseHq() &&
|
||||||
|
subscriptionVersion === this.hqSubscriptionVersion) {
|
||||||
|
this.cardData = cardData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopSubscribeHq(): void {
|
||||||
|
this.hqSubscriptionVersion += 1;
|
||||||
|
if (this.hqUnsubscribe !== undefined) {
|
||||||
|
this.hqUnsubscribe();
|
||||||
|
this.hqUnsubscribe = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private restartSubscription(): void {
|
||||||
|
if (this.cardData.tabs.length > 0) {
|
||||||
|
this.subscribeHq();
|
||||||
|
} else if (this.dataReady) {
|
||||||
|
this.updateCardData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private subscribeTcpNetStatus(): void {
|
||||||
|
if (this.isTcpEventSubscribed || !this.canUseHq()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isTcpEventSubscribed = true;
|
||||||
|
emitter.on(
|
||||||
|
{ eventId: EmitterConstants.NETWORK_TCP_RECONNECT },
|
||||||
|
this.tcpReconnectListener,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unSubscribeTcpNetStatus(): void {
|
||||||
|
if (!this.isTcpEventSubscribed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isTcpEventSubscribed = false;
|
||||||
|
emitter.off(
|
||||||
|
EmitterConstants.NETWORK_TCP_RECONNECT,
|
||||||
|
this.tcpReconnectListener,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private canUseHq(): boolean {
|
||||||
|
return this.isComponentActive && this.firstPageVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isCurrentDataRequest(requestVersion: number): boolean {
|
||||||
|
return this.isComponentActive && requestVersion === this.dataRequestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private showTabGradient(): boolean {
|
||||||
|
const maxOffset: number = this.tabContentWidth - this.tabViewportWidth;
|
||||||
|
return maxOffset > 0 && this.tabScrollOffset < maxOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column() {
|
||||||
|
this.buildTitleBar()
|
||||||
|
if (this.cardData.tabs.length > 0) {
|
||||||
|
this.buildMainContent()
|
||||||
|
} else {
|
||||||
|
this.buildEmptyState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.backgroundColor($r('app.color.surface_layer1_foreground'))
|
||||||
|
.borderRadius(4)
|
||||||
|
.padding({ left: 10, right: 10, bottom: 10 })
|
||||||
|
.bindSheet(this.showExplainSheet, this.buildExplainSheetContent, {
|
||||||
|
height: SheetSize.FIT_CONTENT,
|
||||||
|
dragBar: true,
|
||||||
|
showClose: false,
|
||||||
|
maskColor: $r('app.color.mask_level2'),
|
||||||
|
onDisappear: () => {
|
||||||
|
this.showExplainSheet = false;
|
||||||
|
},
|
||||||
|
radius: {
|
||||||
|
topLeft: 10,
|
||||||
|
topRight: 10,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildTitleBar() {
|
||||||
|
Row() {
|
||||||
|
Row({ space: 4 }) {
|
||||||
|
Text(this.cardConfig?.card_title.value || $r('app.string.ai_pick_title'))
|
||||||
|
.fontSize(18)
|
||||||
|
.fontWeight(500)
|
||||||
|
.fontColor($r('app.color.elements_text_primary_02'))
|
||||||
|
.maxLines(1)
|
||||||
|
|
||||||
|
if ((this.cardConfig?.card_url.android ?? '') !== '' ||
|
||||||
|
(this.cardConfig?.card_url.ios ?? '') !== '') {
|
||||||
|
Image($r('app.media.icon_arrow_forward_20'))
|
||||||
|
.width(20)
|
||||||
|
.height(20)
|
||||||
|
.fillColor($r('app.color.elements_icon_primary_02'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.height('100%')
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToCardTitle();
|
||||||
|
})
|
||||||
|
|
||||||
|
if ((this.cardConfig?.explain_message ?? '') !== '') {
|
||||||
|
Image($r('app.media.icon_info_24'))
|
||||||
|
.width(16)
|
||||||
|
.height(16)
|
||||||
|
.fillColor($r('app.color.elements_icon_tertiary'))
|
||||||
|
.margin({ left: 8 })
|
||||||
|
.onClick(() => {
|
||||||
|
this.showExplainSheet = true;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Blank()
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(48)
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildExplainSheetContent() {
|
||||||
|
Column() {
|
||||||
|
Stack({ alignContent: Alignment.End }) {
|
||||||
|
Text(this.cardConfig?.explain_title ||
|
||||||
|
this.cardConfig?.card_title.value ||
|
||||||
|
$r('app.string.ai_pick_title'))
|
||||||
|
.fontSize(18)
|
||||||
|
.fontWeight(500)
|
||||||
|
.textAlign(TextAlign.Center)
|
||||||
|
.width('100%')
|
||||||
|
|
||||||
|
Image($r('app.media.icon_close_24'))
|
||||||
|
.width(24)
|
||||||
|
.height(24)
|
||||||
|
.fillColor($r('app.color.elements_icon_primary_02'))
|
||||||
|
.onClick(() => {
|
||||||
|
this.showExplainSheet = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.height(64)
|
||||||
|
.width('100%')
|
||||||
|
|
||||||
|
Scroll() {
|
||||||
|
Text((this.cardConfig?.explain_message ?? '')
|
||||||
|
.replace(new RegExp('\\\\n', 'g'), '\n'))
|
||||||
|
.width('100%')
|
||||||
|
.fontSize(14)
|
||||||
|
.lineHeight(22)
|
||||||
|
.fontColor($r('app.color.elements_text_primary_02'))
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.scrollBar(BarState.Off)
|
||||||
|
|
||||||
|
Button($r('app.string.card_explain_confirm'))
|
||||||
|
.type(ButtonType.Normal)
|
||||||
|
.backgroundColor($r('app.color.elements_button_bg_primary'))
|
||||||
|
.width('100%')
|
||||||
|
.height(44)
|
||||||
|
.borderRadius(4)
|
||||||
|
.margin({ top: 16 })
|
||||||
|
.onClick(() => {
|
||||||
|
this.showExplainSheet = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.backgroundColor($r('app.color.surface_layer3_background'))
|
||||||
|
.padding({
|
||||||
|
bottom: px2vp(GlobalContext.navigationBarHeight),
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildMainContent() {
|
||||||
|
Column() {
|
||||||
|
this.buildTabBar()
|
||||||
|
if (this.cardData.tabs[this.tabIdx].items.length > 0) {
|
||||||
|
this.buildStrategyDesc({ tabIdx: this.tabIdx })
|
||||||
|
this.buildTable()
|
||||||
|
} else {
|
||||||
|
this.buildNoContractState()
|
||||||
|
}
|
||||||
|
this.buildCustomStrategy()
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(MAIN_CONTENT_HEIGHT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildTabBar() {
|
||||||
|
Stack({ alignContent: Alignment.End }) {
|
||||||
|
Scroll() {
|
||||||
|
Row({ space: 8 }) {
|
||||||
|
ForEach(this.cardData.tabs, (tab: PickTabData, index: number) => {
|
||||||
|
Text(`${tab.strategy.name}(${tab.strategy.count})`)
|
||||||
|
.fontSize(14)
|
||||||
|
.fontWeight(this.tabIdx === index
|
||||||
|
? FontWeight.Medium
|
||||||
|
: FontWeight.Normal)
|
||||||
|
.fontColor(this.tabIdx === index
|
||||||
|
? $r('app.color.elements_text_primary_01')
|
||||||
|
: $r('app.color.elements_text_secondary'))
|
||||||
|
.constraintSize({ minWidth: 66 })
|
||||||
|
.maxLines(1)
|
||||||
|
.textAlign(TextAlign.Center)
|
||||||
|
.height(TAB_BAR_HEIGHT)
|
||||||
|
.padding({ left: 8, right: 8 })
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor(this.tabIdx === index
|
||||||
|
? $r('app.color.elements_others_bg_primary')
|
||||||
|
: $r('app.color.elements_button_bg_tertiary'))
|
||||||
|
.onClick(() => {
|
||||||
|
this.tabIdx = index;
|
||||||
|
})
|
||||||
|
}, (tab: PickTabData): string =>
|
||||||
|
`${tab.strategy.periodType}_${tab.strategy.id}`)
|
||||||
|
}
|
||||||
|
.onAreaChange((_oldValue: Area, newValue: Area) => {
|
||||||
|
this.tabContentWidth = Number(newValue.width);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(TAB_BAR_HEIGHT)
|
||||||
|
.padding({ right: 10 })
|
||||||
|
.scrollable(ScrollDirection.Horizontal)
|
||||||
|
.scrollBar(BarState.Off)
|
||||||
|
.align(Alignment.Start)
|
||||||
|
.onScroll((xOffset: number, _yOffset: number) => {
|
||||||
|
this.tabScrollOffset = xOffset;
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.showTabGradient()) {
|
||||||
|
Row()
|
||||||
|
.width(24)
|
||||||
|
.height(TAB_BAR_HEIGHT)
|
||||||
|
.linearGradient({
|
||||||
|
angle: 90,
|
||||||
|
colors: [
|
||||||
|
[$r('app.color.transparent_white'), 0],
|
||||||
|
[$r('app.color.surface_layer1_foreground'), 1],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.hitTestBehavior(HitTestMode.None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(TAB_BAR_HEIGHT)
|
||||||
|
.onAreaChange((_oldValue: Area, newValue: Area) => {
|
||||||
|
this.tabViewportWidth = Number(newValue.width);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildStrategyDesc($$: StrategyDescParams) {
|
||||||
|
Row() {
|
||||||
|
Column({ space: 4 }) {
|
||||||
|
if (this.cardData.tabs[$$.tabIdx].strategy.detail !== '') {
|
||||||
|
Text(this.cardData.tabs[$$.tabIdx].strategy.detail)
|
||||||
|
.fontSize(14)
|
||||||
|
.lineHeight(21)
|
||||||
|
.fontColor($r('app.color.elements_text_primary_02'))
|
||||||
|
.maxLines(2)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToStrategyDetail(
|
||||||
|
this.cardData.tabs[$$.tabIdx].strategy,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cardData.tabs[$$.tabIdx].strategy.tagList.length > 0) {
|
||||||
|
Row({ space: 4 }) {
|
||||||
|
ForEach(
|
||||||
|
this.cardData.tabs[$$.tabIdx].strategy.tagList,
|
||||||
|
(tag: string) => {
|
||||||
|
Text(tag)
|
||||||
|
.fontSize(11)
|
||||||
|
.lineHeight(18)
|
||||||
|
.fontColor($r('app.color.elements_text_primary_01'))
|
||||||
|
.maxLines(1)
|
||||||
|
.height(18)
|
||||||
|
.padding({ left: 3, right: 3 })
|
||||||
|
.borderRadius(2)
|
||||||
|
.border({
|
||||||
|
width: 0.5,
|
||||||
|
color: $r('app.color.elements_text_primary_01'),
|
||||||
|
})
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToLabel(
|
||||||
|
this.cardData.tabs[$$.tabIdx].strategy,
|
||||||
|
tag,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
(tag: string): string => tag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layoutWeight(1)
|
||||||
|
.alignItems(HorizontalAlign.Start)
|
||||||
|
|
||||||
|
if ((this.cardData.tabs[$$.tabIdx]
|
||||||
|
.strategy.imgs[this.isDarkMode ? 1 : 0] ?? '') !== '') {
|
||||||
|
Image(this.cardData.tabs[$$.tabIdx]
|
||||||
|
.strategy.imgs[this.isDarkMode ? 1 : 0] ?? '')
|
||||||
|
.width(104)
|
||||||
|
.height(66)
|
||||||
|
.objectFit(ImageFit.Contain)
|
||||||
|
.margin({ left: 10 })
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToStrategyDetail(
|
||||||
|
this.cardData.tabs[$$.tabIdx].strategy,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(STRATEGY_DESC_HEIGHT)
|
||||||
|
.padding({ top: 10, bottom: 10 })
|
||||||
|
.alignItems(VerticalAlign.Top)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildTable() {
|
||||||
|
Column() {
|
||||||
|
Row({ space: 8 }) {
|
||||||
|
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[0]))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
.width('35%')
|
||||||
|
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[1]))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
.layoutWeight(1)
|
||||||
|
.textAlign(TextAlign.End)
|
||||||
|
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[2]))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
.layoutWeight(1)
|
||||||
|
.textAlign(TextAlign.End)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(TABLE_HEADER_HEIGHT)
|
||||||
|
|
||||||
|
ForEach(
|
||||||
|
this.cardData.tabs[this.tabIdx].items,
|
||||||
|
(item: PickContractItem) => {
|
||||||
|
this.buildContractRow(item)
|
||||||
|
},
|
||||||
|
(item: PickContractItem): string =>
|
||||||
|
`${item.contract}_${item.market}_${item.price ?? ''}_${item.priceChg ?? ''}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(TABLE_HEIGHT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildContractRow(item: PickContractItem) {
|
||||||
|
Row({ space: 8 }) {
|
||||||
|
Row({ space: 5 }) {
|
||||||
|
Text(item.name)
|
||||||
|
.fontSize(16)
|
||||||
|
.maxFontSize(16)
|
||||||
|
.minFontSize(9)
|
||||||
|
.fontColor($r('app.color.elements_text_primary_02'))
|
||||||
|
.layoutWeight(1)
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
if (item.isMain) {
|
||||||
|
Text($r('app.string.ai_pick_main_contract'))
|
||||||
|
.fontSize(11)
|
||||||
|
.fontColor($r('app.color.hotlist_orange'))
|
||||||
|
.width(16)
|
||||||
|
.height(16)
|
||||||
|
.textAlign(TextAlign.Center)
|
||||||
|
.borderRadius(2)
|
||||||
|
.backgroundColor($r('app.color.elements_button_bg_tertiary'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('35%')
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
|
||||||
|
Text(cellValue(
|
||||||
|
this.cardData.tabs[this.tabIdx].keysList[1],
|
||||||
|
item,
|
||||||
|
this.cardData.tabs[this.tabIdx].fieldList,
|
||||||
|
))
|
||||||
|
.fontSize(16)
|
||||||
|
.maxFontSize(16)
|
||||||
|
.minFontSize(9)
|
||||||
|
.fontWeight(500)
|
||||||
|
.fontColor(cellColor(
|
||||||
|
this.cardData.tabs[this.tabIdx].keysList[1],
|
||||||
|
item,
|
||||||
|
))
|
||||||
|
.layoutWeight(1)
|
||||||
|
.textAlign(TextAlign.End)
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
|
||||||
|
Text(cellValue(
|
||||||
|
this.cardData.tabs[this.tabIdx].keysList[2],
|
||||||
|
item,
|
||||||
|
this.cardData.tabs[this.tabIdx].fieldList,
|
||||||
|
))
|
||||||
|
.fontSize(16)
|
||||||
|
.maxFontSize(16)
|
||||||
|
.minFontSize(9)
|
||||||
|
.fontWeight(500)
|
||||||
|
.fontColor(cellColor(
|
||||||
|
this.cardData.tabs[this.tabIdx].keysList[2],
|
||||||
|
item,
|
||||||
|
))
|
||||||
|
.layoutWeight(1)
|
||||||
|
.textAlign(TextAlign.End)
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(TABLE_ROW_HEIGHT)
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
.border({
|
||||||
|
width: { top: 1 },
|
||||||
|
color: $r('app.color.elements_others_divider_primary'),
|
||||||
|
})
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToDetail(item);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildCustomStrategy() {
|
||||||
|
Row({ space: 2 }) {
|
||||||
|
Text($r('app.string.ai_pick_custom_strategy'))
|
||||||
|
.fontSize(14)
|
||||||
|
.lineHeight(20)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
Image($r('app.media.icon_arrow_forward_20'))
|
||||||
|
.width(14)
|
||||||
|
.height(14)
|
||||||
|
.fillColor($r('app.color.elements_icon_tertiary'))
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(CUSTOM_STRATEGY_HEIGHT)
|
||||||
|
.justifyContent(FlexAlign.Center)
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
.onClick(() => {
|
||||||
|
this.jumpToNewHome();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildEmptyState() {
|
||||||
|
Column() {
|
||||||
|
if (this.dataReady) {
|
||||||
|
Image(this.isDarkMode ? EMPTY_IMAGE_DARK : EMPTY_IMAGE_LIGHT)
|
||||||
|
.width(120)
|
||||||
|
.height(120)
|
||||||
|
.margin({ bottom: 8 })
|
||||||
|
Text($r('app.string.ai_pick_no_contract'))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
} else {
|
||||||
|
Text($r('app.string.ai_pick_loading'))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(MAIN_CONTENT_HEIGHT)
|
||||||
|
.alignItems(HorizontalAlign.Center)
|
||||||
|
.justifyContent(FlexAlign.Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
buildNoContractState() {
|
||||||
|
Column() {
|
||||||
|
Image(this.isDarkMode ? EMPTY_IMAGE_DARK : EMPTY_IMAGE_LIGHT)
|
||||||
|
.width(100)
|
||||||
|
.height(100)
|
||||||
|
Text($r('app.string.ai_pick_no_contract'))
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor($r('app.color.elements_text_tertiary'))
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height(NO_CONTRACT_HEIGHT)
|
||||||
|
.alignItems(HorizontalAlign.Center)
|
||||||
|
.justifyContent(FlexAlign.Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
private jumpToDetail(item: PickContractItem): void {
|
||||||
|
const contracts: PickContractItem[] = getUniqueContracts(this.cardData);
|
||||||
|
const codeList: string =
|
||||||
|
contracts.map((contract: PickContractItem): string => contract.contract).join(',');
|
||||||
|
const marketIdList: string =
|
||||||
|
contracts.map((contract: PickContractItem): string => contract.market).join(',');
|
||||||
|
const nameList: string =
|
||||||
|
contracts.map((contract: PickContractItem): string => contract.name).join(',');
|
||||||
|
jumpToQuote(item.contract, item.market, codeList, marketIdList, nameList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private jumpToCardTitle(): void {
|
||||||
|
const jumpUrl: string =
|
||||||
|
this.cardConfig?.card_url.android || this.cardConfig?.card_url.ios || '';
|
||||||
|
RouterUtil.jumpPage(jumpUrl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private jumpToNewHome(): void {
|
||||||
|
const pageUrl: string =
|
||||||
|
'https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-home.html?sync=1';
|
||||||
|
RouterUtil.jumpPage(this.buildClientUrl(pageUrl), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private jumpToStrategyDetail(strategy: PickStrategy): void {
|
||||||
|
const pageUrl: string =
|
||||||
|
'https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-detail.html' +
|
||||||
|
`?id=${strategy.id}&type=${strategy.periodType}` +
|
||||||
|
`&name=${encodeURIComponent(strategy.name)}`;
|
||||||
|
RouterUtil.jumpPage(this.buildClientUrl(pageUrl), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private jumpToLabel(strategy: PickStrategy, label: string): void {
|
||||||
|
const pageUrl: string =
|
||||||
|
'https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-label.html' +
|
||||||
|
`?id=${strategy.id}&type=${strategy.periodType}` +
|
||||||
|
`&label=${encodeURIComponent(label)}`;
|
||||||
|
RouterUtil.jumpPage(this.buildClientUrl(pageUrl), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildClientUrl(pageUrl: string): string {
|
||||||
|
const title: string =
|
||||||
|
this.cardConfig?.card_title.value ||
|
||||||
|
getResourceString(getContext(this), $r('app.string.ai_pick_title'));
|
||||||
|
return 'client.html?action=ymtz^ishiddenbar=1^mode=new^webid=2804^' +
|
||||||
|
`url=${pageUrl}^title=${title}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
import {
|
||||||
|
CardData,
|
||||||
|
PickContractItem,
|
||||||
|
PickFieldItem,
|
||||||
|
PickTabData,
|
||||||
|
RawDetailItem,
|
||||||
|
RawFieldItem,
|
||||||
|
RawResultItem,
|
||||||
|
RawStrategyItem,
|
||||||
|
} from './AiPickModels';
|
||||||
|
import { BLACK_LIST, MAX_DOUBLE_KEYS, MAX_SHOW_KEYS } from './AiPickConstant';
|
||||||
|
import { formatPercent, formatPrice, riseFallColor } from '../common/NumberFormat';
|
||||||
|
|
||||||
|
const EMPTY_EXTRA_FIELDS: Record<string, string> = {};
|
||||||
|
|
||||||
|
export interface ResData {
|
||||||
|
showKeyList: string[];
|
||||||
|
fieldList: PickFieldItem[];
|
||||||
|
resultValueList: Record<string, string>[];
|
||||||
|
list: RawDetailItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterFieldKey(key: string): boolean {
|
||||||
|
const extBlackList: string[] =
|
||||||
|
BLACK_LIST.map((value: string): string => `期货@${value}`);
|
||||||
|
const inBlack: boolean = BLACK_LIST.includes(key) || extBlackList.includes(key);
|
||||||
|
const isChangeField: boolean =
|
||||||
|
key.startsWith('涨跌幅[') || key.startsWith('期货@涨跌幅[');
|
||||||
|
return !inBlack && !isChangeField;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatBigData(value: number | string): string {
|
||||||
|
const numericValue: number = Number(value);
|
||||||
|
if (Number.isNaN(numericValue)) {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
|
const tenThousand = 1e4;
|
||||||
|
const billion = 1e8;
|
||||||
|
const trillion = 1e12;
|
||||||
|
const formattedValue: number = Number(numericValue.toFixed(3));
|
||||||
|
const absoluteValue: number = Math.abs(formattedValue);
|
||||||
|
if (absoluteValue >= trillion) {
|
||||||
|
return `${(formattedValue / trillion).toFixed(2)}万亿`;
|
||||||
|
}
|
||||||
|
if (absoluteValue >= billion) {
|
||||||
|
if (Number((absoluteValue / billion).toFixed(2)) >= tenThousand) {
|
||||||
|
return `${(formattedValue / trillion).toFixed(2)}万亿`;
|
||||||
|
}
|
||||||
|
return `${(formattedValue / billion).toFixed(2)}亿`;
|
||||||
|
}
|
||||||
|
if (absoluteValue >= tenThousand) {
|
||||||
|
if (Number((absoluteValue / tenThousand).toFixed(2)) >= tenThousand) {
|
||||||
|
return `${(formattedValue / billion).toFixed(2)}亿`;
|
||||||
|
}
|
||||||
|
return `${(formattedValue / tenThousand).toFixed(2)}万`;
|
||||||
|
}
|
||||||
|
return `${formattedValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShowKeyList(doubleKeys: string[]): string[] {
|
||||||
|
if (doubleKeys.length === 0) {
|
||||||
|
return ['合约名称', '最新价', '涨跌幅(结)'];
|
||||||
|
}
|
||||||
|
return ['合约名称']
|
||||||
|
.concat(doubleKeys.concat(['涨跌幅(结)', '最新价']))
|
||||||
|
.slice(0, MAX_SHOW_KEYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getResultValueList(
|
||||||
|
resultList: RawResultItem[],
|
||||||
|
doubleListKeys: string[],
|
||||||
|
): Record<string, string>[] {
|
||||||
|
return resultList.map((value: RawResultItem): Record<string, string> => {
|
||||||
|
const result: Record<string, string> = {};
|
||||||
|
doubleListKeys.forEach((key: string): void => {
|
||||||
|
const rawValue: number | string | undefined = value[key];
|
||||||
|
if (rawValue !== undefined) {
|
||||||
|
result[key] = formatBigData(rawValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getResData(strategyItem: RawStrategyItem): ResData {
|
||||||
|
const rawFieldList: RawFieldItem[] = strategyItem.field_list ?? [];
|
||||||
|
const resultList: RawResultItem[] = strategyItem.origin_result_list ?? [];
|
||||||
|
const list: RawDetailItem[] = strategyItem.detail_list ?? [];
|
||||||
|
const doubleList: RawFieldItem[] = rawFieldList.filter(
|
||||||
|
(field: RawFieldItem): boolean =>
|
||||||
|
field.type === 'DOUBLE' && filterFieldKey(field.key),
|
||||||
|
);
|
||||||
|
const doubleListKeys: string[] =
|
||||||
|
doubleList.map((field: RawFieldItem): string => field.key);
|
||||||
|
const showKeys: string[] = doubleListKeys.slice(0, MAX_DOUBLE_KEYS);
|
||||||
|
const fieldList: PickFieldItem[] =
|
||||||
|
doubleList.map((field: RawFieldItem): PickFieldItem => {
|
||||||
|
const item: PickFieldItem = {
|
||||||
|
key: field.key,
|
||||||
|
unit: field.unit ?? '',
|
||||||
|
};
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
const result: ResData = {
|
||||||
|
showKeyList: getShowKeyList(showKeys),
|
||||||
|
fieldList: fieldList,
|
||||||
|
resultValueList: getResultValueList(resultList, doubleListKeys),
|
||||||
|
list: list,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTableList(
|
||||||
|
list: RawDetailItem[],
|
||||||
|
resultValueList: Record<string, string>[],
|
||||||
|
): PickContractItem[] {
|
||||||
|
return list.map((item: RawDetailItem, index: number): PickContractItem => {
|
||||||
|
const contract: PickContractItem = {
|
||||||
|
contract: item.contract,
|
||||||
|
market: item.market,
|
||||||
|
name: item.contract_name,
|
||||||
|
isMain: String(item.main_contract_type) === '1',
|
||||||
|
extraFields: resultValueList[index] ?? EMPTY_EXTRA_FIELDS,
|
||||||
|
};
|
||||||
|
return contract;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUniqueContracts(cardData: CardData): PickContractItem[] {
|
||||||
|
const contracts: PickContractItem[] = [];
|
||||||
|
const contractKeys: string[] = [];
|
||||||
|
cardData.tabs.forEach((tab: PickTabData): void => {
|
||||||
|
tab.items.forEach((item: PickContractItem): void => {
|
||||||
|
const contractKey: string = `${item.contract}_${item.market}`;
|
||||||
|
if (!contractKeys.includes(contractKey)) {
|
||||||
|
contractKeys.push(contractKey);
|
||||||
|
contracts.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return contracts;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatColName(name: string): string {
|
||||||
|
return name
|
||||||
|
.replace(new RegExp('期货@', 'g'), '')
|
||||||
|
.replace(new RegExp('\\[.*?\\]', 'g'), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUnit(key: string, fieldList: PickFieldItem[]): string {
|
||||||
|
const item: PickFieldItem | undefined =
|
||||||
|
fieldList.find((field: PickFieldItem): boolean => field.key === key);
|
||||||
|
return item?.unit ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cellValue(
|
||||||
|
columnName: string,
|
||||||
|
item: PickContractItem,
|
||||||
|
fieldList: PickFieldItem[],
|
||||||
|
): string {
|
||||||
|
if (columnName === '最新价') {
|
||||||
|
return formatPrice(item.price);
|
||||||
|
}
|
||||||
|
if (columnName === '涨跌幅(结)') {
|
||||||
|
return formatPercent(item.priceChg);
|
||||||
|
}
|
||||||
|
const rawValue: string = item.extraFields[columnName] ?? '--';
|
||||||
|
const unit: string = getUnit(columnName, fieldList);
|
||||||
|
return unit === '' ? rawValue : `${rawValue}${unit}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cellColor(columnName: string, item: PickContractItem): Resource {
|
||||||
|
if (columnName === '最新价' || columnName === '涨跌幅(结)') {
|
||||||
|
return riseFallColor(item.priceChg);
|
||||||
|
}
|
||||||
|
return $r('app.color.elements_text_primary_02');
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import { AiRiseFallNodeComponent } from '../../node/view/AiRiseFallNodeComponent
|
|||||||
import { HotListNodeComponent } from '../../node/view/HotListNodeComponent'
|
import { HotListNodeComponent } from '../../node/view/HotListNodeComponent'
|
||||||
import { CommodityOptionsNodeComponent } from '../../node/view/CommodityOptionsNodeComponent'
|
import { CommodityOptionsNodeComponent } from '../../node/view/CommodityOptionsNodeComponent'
|
||||||
import { MarketRankingNodeComponent } from '../../market-ranking/MarketRankingNodeComponent'
|
import { MarketRankingNodeComponent } from '../../market-ranking/MarketRankingNodeComponent'
|
||||||
|
import { AiPickNodeComponent } from '../../ai-pick/AiPickNodeComponent'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,11 +147,14 @@ export struct FirstPage {
|
|||||||
aiRiseFallModel.targetTypeId = FirstPageConstant.KEY_AI_RISEFALL.toString()
|
aiRiseFallModel.targetTypeId = FirstPageConstant.KEY_AI_RISEFALL.toString()
|
||||||
let marketRankingModel = new FirstPageNodeModel()
|
let marketRankingModel = new FirstPageNodeModel()
|
||||||
marketRankingModel.targetTypeId = FirstPageConstant.KEY_MARKET_RANKING.toString()
|
marketRankingModel.targetTypeId = FirstPageConstant.KEY_MARKET_RANKING.toString()
|
||||||
|
let aiPickModel = new FirstPageNodeModel()
|
||||||
|
aiPickModel.targetTypeId = FirstPageConstant.KEY_AI_PICK.toString()
|
||||||
let feedBackModel = new FirstPageNodeModel()
|
let feedBackModel = new FirstPageNodeModel()
|
||||||
feedBackModel.targetTypeId = FirstPageConstant.KEY_FEED_BACK.toString()
|
feedBackModel.targetTypeId = FirstPageConstant.KEY_FEED_BACK.toString()
|
||||||
let endTipModel = new FirstPageNodeModel()
|
let endTipModel = new FirstPageNodeModel()
|
||||||
endTipModel.targetTypeId = FirstPageConstant.KEY_END_TIP.toString()
|
endTipModel.targetTypeId = FirstPageConstant.KEY_END_TIP.toString()
|
||||||
response.push(hotListModel, commodityOptionModel, aiRiseFallModel, aiDiagnosisModel, marketRankingModel, feedBackModel, endTipModel)
|
response.push(hotListModel, commodityOptionModel, aiRiseFallModel, aiDiagnosisModel,
|
||||||
|
aiPickModel, marketRankingModel, feedBackModel, endTipModel)
|
||||||
this.nodeModelArr = response
|
this.nodeModelArr = response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,6 +362,10 @@ export struct FirstPage {
|
|||||||
MarketRankingNodeComponent({
|
MarketRankingNodeComponent({
|
||||||
refreshTrigger: this.refreshTrigger,
|
refreshTrigger: this.refreshTrigger,
|
||||||
}).margin({ top: index > 0 ? 10 : 0, left: 10, right: 10 })
|
}).margin({ top: index > 0 ? 10 : 0, left: 10, right: 10 })
|
||||||
|
} else if (model.targetTypeId == FirstPageConstant.KEY_AI_PICK.toString()) {
|
||||||
|
AiPickNodeComponent({
|
||||||
|
refreshTrigger: this.refreshTrigger,
|
||||||
|
}).margin({ top: index > 0 ? 10 : 0, left: 10, right: 10 })
|
||||||
} else if (model.targetTypeId == FirstPageConstant.KEY_TOP_BG.toString()) {
|
} else if (model.targetTypeId == FirstPageConstant.KEY_TOP_BG.toString()) {
|
||||||
TopImageNodeComponent({
|
TopImageNodeComponent({
|
||||||
nodeModel: model
|
nodeModel: model
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ export class FirstPageConstant {
|
|||||||
static KEY_HOT_LIST = 18 //热点排行
|
static KEY_HOT_LIST = 18 //热点排行
|
||||||
static KEY_COMMODITY_OPTIONS = 19 //商品期权
|
static KEY_COMMODITY_OPTIONS = 19 //商品期权
|
||||||
static KEY_MARKET_RANKING = 20 //市场排名
|
static KEY_MARKET_RANKING = 20 //市场排名
|
||||||
|
static KEY_AI_PICK = 21 //AI选期
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ export class TableConstants {
|
|||||||
static readonly DATA_ID_CHG_SPEED_1MIN: number = 34874; // 1 分钟涨跌速
|
static readonly DATA_ID_CHG_SPEED_1MIN: number = 34874; // 1 分钟涨跌速
|
||||||
static readonly DATA_ID_CHG_SPEED_10MIN: number = 34875; // 10 分钟涨跌速
|
static readonly DATA_ID_CHG_SPEED_10MIN: number = 34875; // 10 分钟涨跌速
|
||||||
static readonly DATA_ID_CHG_SPEED_15MIN: number = 34876; // 15 分钟涨跌速
|
static readonly DATA_ID_CHG_SPEED_15MIN: number = 34876; // 15 分钟涨跌速
|
||||||
static readonly DATA_ID_ZF_YTD: number = 34877; // 昨收涨跌幅
|
static readonly DATA_ID_ZF_YTD: number = 34877; // 涨跌幅(昨结算价基准)
|
||||||
static readonly DATA_ID_MARKET: number = 36103; // 市场 ID
|
static readonly DATA_ID_MARKET: number = 36103; // 市场 ID
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import { AbsRowData, RowData, TableData } from '@b2b/hq_table';
|
||||||
|
import { TableRequestClient } from '@b2c/lib_baseui';
|
||||||
|
import { RequestHelper, StuffTableStruct } from '@kernel/lib_communication';
|
||||||
|
import {
|
||||||
|
CardData,
|
||||||
|
PickContractItem,
|
||||||
|
PickTabData,
|
||||||
|
} from '../../ai-pick/AiPickModels';
|
||||||
|
import { getUniqueContracts } from '../../ai-pick/AiPickUtils';
|
||||||
|
import { TableConstants } from '../../market-ranking/TableConstants';
|
||||||
|
|
||||||
|
const FRAME_ID = 2201;
|
||||||
|
const PAGE_ID = 4106;
|
||||||
|
const FIELD_IDS: number[] = [
|
||||||
|
TableConstants.DATA_ID_CODE_4,
|
||||||
|
TableConstants.DATA_ID_PRICE,
|
||||||
|
TableConstants.DATA_ID_MARKET,
|
||||||
|
TableConstants.DATA_ID_ZF_YTD,
|
||||||
|
];
|
||||||
|
|
||||||
|
interface AiPickQuote {
|
||||||
|
price?: number;
|
||||||
|
priceChg?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AiPickCardDataListener = (cardData: CardData) => void;
|
||||||
|
|
||||||
|
class AiPickTableRequestClient extends TableRequestClient {
|
||||||
|
private requestParams: string;
|
||||||
|
private sourceCardData: CardData;
|
||||||
|
private listener: AiPickCardDataListener;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
requestParams: string,
|
||||||
|
sourceCardData: CardData,
|
||||||
|
listener: AiPickCardDataListener,
|
||||||
|
) {
|
||||||
|
super(FRAME_ID, PAGE_ID, [], null, undefined);
|
||||||
|
this.requestParams = requestParams;
|
||||||
|
this.sourceCardData = sourceCardData;
|
||||||
|
this.listener = listener;
|
||||||
|
this.setSubscribeIds(FIELD_IDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected doRequest(): void {
|
||||||
|
RequestHelper.getHqManager()
|
||||||
|
.addRequestStructToBuff(this.frameId, this.pageId, this.getInstanceId(), this.requestParams);
|
||||||
|
RequestHelper.build().base(this.frameId, this.pageId, this, this.requestParams).request();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected afterParserModel(tableData: TableData, _structData: StuffTableStruct): void {
|
||||||
|
const quoteMap: Map<string, AiPickQuote> = this.toQuoteMap(tableData);
|
||||||
|
const nextCardData: CardData = this.mergeQuoteData(quoteMap);
|
||||||
|
this.sourceCardData = nextCardData;
|
||||||
|
this.listener(nextCardData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private toQuoteMap(tableData: TableData): Map<string, AiPickQuote> {
|
||||||
|
const quoteMap: Map<string, AiPickQuote> = new Map<string, AiPickQuote>();
|
||||||
|
if (!tableData.tableRows) {
|
||||||
|
return quoteMap;
|
||||||
|
}
|
||||||
|
tableData.tableRows.forEach((row: AbsRowData): void => {
|
||||||
|
if (!(row instanceof RowData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const codeValue = row.originalData.get(TableConstants.DATA_ID_CODE_4);
|
||||||
|
const marketValue = row.originalData.get(TableConstants.DATA_ID_MARKET);
|
||||||
|
if (codeValue === undefined || marketValue === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const quote: AiPickQuote = {
|
||||||
|
price: this.getNumber(row, TableConstants.DATA_ID_PRICE),
|
||||||
|
priceChg: this.getNumber(row, TableConstants.DATA_ID_ZF_YTD),
|
||||||
|
};
|
||||||
|
quoteMap.set(`${codeValue}_${marketValue}`, quote);
|
||||||
|
});
|
||||||
|
return quoteMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mergeQuoteData(quoteMap: Map<string, AiPickQuote>): CardData {
|
||||||
|
const tabs: PickTabData[] =
|
||||||
|
this.sourceCardData.tabs.map((tab: PickTabData): PickTabData => {
|
||||||
|
const items: PickContractItem[] =
|
||||||
|
tab.items.map((item: PickContractItem): PickContractItem => {
|
||||||
|
const quote: AiPickQuote | undefined =
|
||||||
|
quoteMap.get(`${item.contract}_${item.market}`);
|
||||||
|
const nextItem: PickContractItem = {
|
||||||
|
contract: item.contract,
|
||||||
|
market: item.market,
|
||||||
|
name: item.name,
|
||||||
|
price: quote?.price ?? item.price,
|
||||||
|
priceChg: quote?.priceChg ?? item.priceChg,
|
||||||
|
isMain: item.isMain,
|
||||||
|
extraFields: item.extraFields,
|
||||||
|
};
|
||||||
|
return nextItem;
|
||||||
|
});
|
||||||
|
const nextTab: PickTabData = {
|
||||||
|
strategy: tab.strategy,
|
||||||
|
items: items,
|
||||||
|
keysList: tab.keysList,
|
||||||
|
fieldList: tab.fieldList,
|
||||||
|
};
|
||||||
|
return nextTab;
|
||||||
|
});
|
||||||
|
const nextCardData: CardData = { tabs: tabs };
|
||||||
|
return nextCardData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNumber(row: RowData, fieldId: number): number | undefined {
|
||||||
|
const value = row.originalData.get(fieldId);
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && value !== '' && value !== '--') {
|
||||||
|
const result: number = parseFloat(value);
|
||||||
|
return Number.isNaN(result) ? undefined : result;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AiPickHqRequestClient {
|
||||||
|
private static readonly instance: AiPickHqRequestClient =
|
||||||
|
new AiPickHqRequestClient();
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInstance(): AiPickHqRequestClient {
|
||||||
|
return AiPickHqRequestClient.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeHq(cardData: CardData, listener: AiPickCardDataListener): () => void {
|
||||||
|
const contracts: PickContractItem[] = getUniqueContracts(cardData);
|
||||||
|
if (contracts.length === 0) {
|
||||||
|
return (): void => {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const requestParams: string = this.buildRequestParams(contracts);
|
||||||
|
const requestClient: AiPickTableRequestClient =
|
||||||
|
new AiPickTableRequestClient(requestParams, cardData, listener);
|
||||||
|
requestClient.request(0, contracts.length);
|
||||||
|
return (): void => {
|
||||||
|
requestClient.release(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildRequestParams(contracts: PickContractItem[]): string {
|
||||||
|
return `\r\nsortid=-1\r\n` +
|
||||||
|
`sortorder=0\r\n` +
|
||||||
|
`push=1\r\n` +
|
||||||
|
`dataitem=${FIELD_IDS.map((fieldId: number): string => `${fieldId},`).join('')}\r\n` +
|
||||||
|
`codelist=${this.formatCodeList(contracts)}\r\n` +
|
||||||
|
`update=1\r\n` +
|
||||||
|
`scenario=qht_qihuo_sort\r\n` +
|
||||||
|
`precision=1\r\n` +
|
||||||
|
`pushtime=2.5\r\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatCodeList(contracts: PickContractItem[]): string {
|
||||||
|
const markets: string[] = [];
|
||||||
|
const codeGroups: string[][] = [];
|
||||||
|
contracts.forEach((contract: PickContractItem): void => {
|
||||||
|
const marketIndex: number = markets.indexOf(contract.market);
|
||||||
|
if (marketIndex >= 0) {
|
||||||
|
codeGroups[marketIndex].push(contract.contract);
|
||||||
|
} else {
|
||||||
|
markets.push(contract.market);
|
||||||
|
codeGroups.push([contract.contract]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return markets.map((market: string, index: number): string =>
|
||||||
|
`${market}(${codeGroups[index].map((code: string): string => `${code},`).join('')});`
|
||||||
|
).join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ export interface FirstPageCardsConfig {
|
|||||||
aiRiseFall: AllCardsCard
|
aiRiseFall: AllCardsCard
|
||||||
commodityOptions: CommodityOptionsCardConfig
|
commodityOptions: CommodityOptionsCardConfig
|
||||||
marketRanking: AllCardsCard
|
marketRanking: AllCardsCard
|
||||||
|
aiPick: AllCardsCard
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FirstPageCardsConfigLoader {
|
export class FirstPageCardsConfigLoader {
|
||||||
|
|||||||
@@ -279,6 +279,34 @@
|
|||||||
{
|
{
|
||||||
"name": "market_ranking_15min_fall_speed",
|
"name": "market_ranking_15min_fall_speed",
|
||||||
"value": "15分钟跌速"
|
"value": "15分钟跌速"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_title",
|
||||||
|
"value": "AI选期"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_loading",
|
||||||
|
"value": "数据加载中"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_no_contract",
|
||||||
|
"value": "没有符合的合约"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_main_contract",
|
||||||
|
"value": "主"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_strategy",
|
||||||
|
"value": "一句话定制策略"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_tag",
|
||||||
|
"value": "自编"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "card_explain_confirm",
|
||||||
|
"value": "我知道了"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,34 @@
|
|||||||
{
|
{
|
||||||
"name": "page_show",
|
"name": "page_show",
|
||||||
"value": "page from package"
|
"value": "page from package"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_title",
|
||||||
|
"value": "AI Picks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_loading",
|
||||||
|
"value": "Loading"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_no_contract",
|
||||||
|
"value": "No matching contracts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_main_contract",
|
||||||
|
"value": "M"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_strategy",
|
||||||
|
"value": "Create a strategy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_tag",
|
||||||
|
"value": "Custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "card_explain_confirm",
|
||||||
|
"value": "Got it"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -112,5 +112,31 @@
|
|||||||
"explain_message": null,
|
"explain_message": null,
|
||||||
"mod_data": [],
|
"mod_data": [],
|
||||||
"bury_point": "marketRank"
|
"bury_point": "marketRank"
|
||||||
|
},
|
||||||
|
"aiPick": {
|
||||||
|
"card_id": 5106,
|
||||||
|
"card_key": "futuresHomepageAIPickCard",
|
||||||
|
"title_type": 1,
|
||||||
|
"is_toggle": false,
|
||||||
|
"render_key": "ai-pick",
|
||||||
|
"card_size": 4,
|
||||||
|
"explain_title": "AI选期说明",
|
||||||
|
"explain_message": "1.合约数据更新说明:合约最新价和涨跌幅信息实时更新,无需手动刷新。\n2.符合策略的合约内容需要手动刷新。\n3.安卓3.96.1版本和苹果3.97.01版本以上已支持自定义选期。\n4.新版首页AI选期将与AI分组同步,默认取AI分组前5条。\n5.新版首页AI选期策略可在AI分组管理中可进行删减。",
|
||||||
|
"card_title": {
|
||||||
|
"value": "AI选期",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"card_url": {
|
||||||
|
"ios": "",
|
||||||
|
"android": ""
|
||||||
|
},
|
||||||
|
"mod_data": [
|
||||||
|
{
|
||||||
|
"url": "https://ftapi.10jqka.com.cn/futgwapi/api/ai_diagnosis/home_strategy/v1/user_data",
|
||||||
|
"param_list": [""],
|
||||||
|
"req_desc": "http_get"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bury_point": "aiPick"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,34 @@
|
|||||||
{
|
{
|
||||||
"name": "page_show",
|
"name": "page_show",
|
||||||
"value": "page from package"
|
"value": "page from package"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_title",
|
||||||
|
"value": "AI选期"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_loading",
|
||||||
|
"value": "数据加载中"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_no_contract",
|
||||||
|
"value": "没有符合的合约"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_main_contract",
|
||||||
|
"value": "主"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_strategy",
|
||||||
|
"value": "一句话定制策略"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ai_pick_custom_tag",
|
||||||
|
"value": "自编"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "card_explain_confirm",
|
||||||
|
"value": "我知道了"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user