Initial commit: @b2c/first_page HarmonyOS stage-mode HAR

## Repository
- HarmonyOS stage-mode HAR module, consumed by host app
- No standalone build — hvigorw, oh_modules, and ../biz_quote absent here

## Architecture
### Feed Cards
- FirstPage.createNodeView dispatches cards by FirstPageConstant.KEY_*
- Card config from first_page_cards_config.json loaded by FirstPageCardsConfigLoader
- Public entrypoint: Index.ets

### Market Ranking (src/main/ets/market-ranking/)
- HTTP: MarketRankingDataFetcher fetches recommend_futures contracts
- HQ: MarketRankingHqRequestClient subscribes via 4106/frame 2201/scenario qht_qihuo_sort
- TCP reconnect rebuilds subscription from original HTTP contract list
- Async callbacks guarded by request/subscription versions

### Other Cards
- HotList, CommodityOptions, AiDiagnosis, AiRiseFall, SelfSelectedStock, CustomEntryList, FourEntryList
- Each has DataFetcher + HQ request client + NodeComponent

## Key Changes (from CardHarmonyOS reference)
- Added MetricId, PeriodId, SortOrder enums (MarketRankingModels.ets)
- Replaced string comparisons with enum switches in thirdColumnTitle/Value/Color
- Removed redundant undefined guards in thirdColumnColor/priceColor, delegated to riseFallColor
- Inlined priceColor(), use riseFallColor directly
- Tab bar Stack.height(30), Alignment.End (reverted from 46/TopEnd)
- Name column fontWeight removed (default regular)
This commit is contained in:
clz
2026-07-26 01:10:20 +08:00
commit af0025ce38
137 changed files with 13358 additions and 0 deletions
@@ -0,0 +1,152 @@
import { HXUtils, StockMarketUtil } from "@b2c/lib_baseui";
import { NumberUtils } from "hxutil";
export interface SelfSelectedStockModel {
id: string;
stockCode: string;
stockName: string;
currentPrice: number;
changeAmount: number;
changePercent: number;
updateTime?: number;
}
export interface SelfSelectedStockModelMock {
code: string;
name: string;
price: number;
change: number;
percent: number;
}
export interface StockGroupModel {
id: string;
groupName: string;
stocks: SelfSelectedStockModel[];
isSelected: boolean;
}
const HQ_DATA_INVALID = "--"
@Observed
export class SwiperHqDataItemModel extends Array<GridHqDataItemModel>{}
@Observed
export class GridHqDataItemModel extends Array<HQDataItemModel>{}
/**
* 行情自定义数据结构
*/
@Observed
export class HQDataItemModel{
name:string = HQ_DATA_INVALID
code:string = HQ_DATA_INVALID
showCode:string = HQ_DATA_INVALID
market:string = "0"
price:string = HQ_DATA_INVALID
riseFail:string = HQ_DATA_INVALID //涨跌幅
priceRise:string = HQ_DATA_INVALID
nameColor:ResourceColor = 0
priceColor:ResourceColor = 0
riseFailColor:ResourceColor = 0
priceRiseColor:ResourceColor = 0
showAddImage:boolean = false
/**
* -1:跌,1:涨,0:不变
*/
riseFailFlag:number = 0
priceChange:boolean = false
constructor(stockCode?:string,stockMarket?:string,stockName?:string) {
this.code = stockCode??HQ_DATA_INVALID
this.market = stockMarket??HQ_DATA_INVALID
this.name = stockName??HQ_DATA_INVALID
this.showCode = stockCode??HQ_DATA_INVALID
}
copyFrom(dataItemModel:HQDataItemModel){
this.name = dataItemModel.name
const market = dataItemModel.market
const showCodeTemp = dataItemModel.showCode
if ((StockMarketUtil.isOption(market+"") || StockMarketUtil.isFutureTL(market+"")) && HXUtils.isValidContractData(showCodeTemp+"")) {
this.showCode = showCodeTemp
}else{
this.showCode = this.code
}
this.riseFailFlag = this.getRiseFailFlag(dataItemModel.price,this.price)
this.priceChange = this.riseFailFlag != 0
this.price = dataItemModel.price
this.riseFail = dataItemModel.riseFail
this.priceRise = dataItemModel.priceRise
this.riseFailColor = dataItemModel.riseFailColor
this.riseFailColor = dataItemModel.riseFailColor
}
getMapKey():string{
return this.code+"_"+this.market
}
private getRiseFailFlag(newPrice:string,lastPrice:string){
let ret = 0
if (newPrice == lastPrice){
return ret
}
if (NumberUtils.isNumeric(newPrice) && NumberUtils.isNumeric(lastPrice)) {
const newPriceNumber = parseFloat(newPrice)
const lastPriceNumber = parseFloat(lastPrice)
if (newPriceNumber == lastPriceNumber) {
ret = 0
}else {
ret = newPriceNumber > lastPriceNumber ? 1:-1
}
}
return ret
}
copy():HQDataItemModel{
let copyDataItemModel = new HQDataItemModel(this.code,this.market,this.name)
copyDataItemModel.showCode = this.showCode
copyDataItemModel.price = this.price
copyDataItemModel.riseFail = this.riseFail
copyDataItemModel.priceRise = this.priceRise
copyDataItemModel.nameColor = this.nameColor
copyDataItemModel.riseFailColor = this.riseFailColor
copyDataItemModel.priceRiseColor = this.priceRiseColor
copyDataItemModel.showAddImage = this.showAddImage
copyDataItemModel.priceChange = this.priceChange
copyDataItemModel.riseFailFlag = this.riseFailFlag
return copyDataItemModel
}
}
export class GridDataSource implements IDataSource{
private dataArray:HQDataItemModel[] = []
constructor(data:HQDataItemModel[]) {
this.dataArray = data
}
totalCount(): number {
return this.dataArray.length
}
getData(index: number): HQDataItemModel {
return this.dataArray[index]
}
registerDataChangeListener(listener: DataChangeListener): void {
}
unregisterDataChangeListener(listener: DataChangeListener): void {
}
}