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:
@@ -0,0 +1,37 @@
|
||||
type DatePattern = 'yyyyMMdd' | 'yyyy-MM-dd' | 'yyyyMMddHHmmss' | 'HHmmss' | 'MM月dd日' | 'MM-dd HH:mm'
|
||||
|
||||
export class DateUtils {
|
||||
|
||||
static formatDate(date: Date, pattern: DatePattern = 'yyyyMMdd'): string {
|
||||
let year = ""
|
||||
let month = ""
|
||||
let day = ""
|
||||
let hour = ""
|
||||
let minute = ""
|
||||
let second = ""
|
||||
try {
|
||||
year = date.getFullYear().toString();
|
||||
month = ('0' + (date.getMonth() + 1)).slice(-2);
|
||||
day = ('0' + date.getDate()).slice(-2);
|
||||
hour = ('0' + date.getHours()).slice(-2);
|
||||
minute = ('0' + date.getMinutes()).slice(-2);
|
||||
second = ('0' + date.getSeconds()).slice(-2);
|
||||
} catch (e) {
|
||||
//日志获取失败
|
||||
return ""
|
||||
}
|
||||
if (pattern == 'yyyy-MM-dd') {
|
||||
return year + '-' + month + '-' + day
|
||||
} else if (pattern == 'yyyyMMddHHmmss') {
|
||||
return year + month + day + hour + minute + second
|
||||
} else if (pattern == 'HHmmss') {
|
||||
return hour + minute + second
|
||||
} else if (pattern == 'MM月dd日') {
|
||||
return month + '月' + day + '日'
|
||||
} else if (pattern == 'MM-dd HH:mm') {
|
||||
return month + '-' + day + ' ' + hour + ':' + minute
|
||||
} else {
|
||||
return year + month + day
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import { GlobalContext, HXLog } from 'biz_common'
|
||||
import fs from '@ohos.file.fs';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
|
||||
export class FileManager {
|
||||
private static readonly TAG = 'CacheManager'
|
||||
private static instance: FileManager
|
||||
|
||||
public static getInstance() {
|
||||
if (!FileManager.instance) {
|
||||
FileManager.instance = new FileManager()
|
||||
}
|
||||
return FileManager.instance
|
||||
}
|
||||
|
||||
saveCacheSync(saveContent: string, cacheDirPath: string): boolean {
|
||||
let file: fs.File | null = null
|
||||
try {
|
||||
let filePath = this.getCachePathSync(cacheDirPath)
|
||||
if (filePath) {
|
||||
file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC)
|
||||
if (saveContent.length > 0) {
|
||||
fs.writeSync(file.fd, saveContent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError
|
||||
HXLog.e(FileManager.TAG, `error code + ${e.code}`)
|
||||
} finally {
|
||||
if (file !== null) {
|
||||
try {
|
||||
fs.closeSync(file)
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError
|
||||
HXLog.e(FileManager.TAG, `close file error code + ${e.code}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
loadCacheSync(cacheDirPath: string): string {
|
||||
try {
|
||||
let filePath = this.getCachePathSync(cacheDirPath)
|
||||
if (filePath) {
|
||||
let content = fs.readTextSync(filePath)
|
||||
if (content) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
let error: BusinessError = e as BusinessError;
|
||||
HXLog.e(FileManager.TAG, `error code ${error.code} and message ${error.message}`)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
saveFilesSync(saveContent: string, cacheDirPath: string): boolean {
|
||||
let file: fs.File | null = null
|
||||
try {
|
||||
let filePath = this.getFilesPathSync(cacheDirPath)
|
||||
if (filePath) {
|
||||
file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC)
|
||||
if (saveContent.length > 0) {
|
||||
fs.writeSync(file.fd, saveContent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError
|
||||
HXLog.e(FileManager.TAG, `error code + ${e.code}`)
|
||||
} finally {
|
||||
if (file !== null) {
|
||||
try {
|
||||
fs.closeSync(file)
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError
|
||||
HXLog.e(FileManager.TAG, `close file error code + ${e.code}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
loadFilesSync(cacheDirPath: string): string {
|
||||
try {
|
||||
let filePath = this.getFilesPathSync(cacheDirPath)
|
||||
if (filePath) {
|
||||
let content = fs.readTextSync(filePath)
|
||||
if (content) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
let error: BusinessError = e as BusinessError;
|
||||
HXLog.e(FileManager.TAG, `error code ${error.code} and message ${error.message}`)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
public getCachePathSync(cacheDirPath: string): string | null {
|
||||
try {
|
||||
let context = GlobalContext.get()
|
||||
let index = cacheDirPath.lastIndexOf('/')
|
||||
let dirPath = context.cacheDir + cacheDirPath.substring(0, index)
|
||||
let savePath = context.cacheDir + cacheDirPath
|
||||
let res = fs.accessSync(dirPath)
|
||||
if (!res) {
|
||||
fs.mkdirSync(dirPath, true)
|
||||
}
|
||||
return savePath
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError;
|
||||
HXLog.e(FileManager.TAG, `error code + ${e.code}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
public getFilesPathSync(cacheDirPath: string): string | null {
|
||||
try {
|
||||
let context = GlobalContext.get()
|
||||
let index = cacheDirPath.lastIndexOf('/')
|
||||
let dirPath = context.filesDir + cacheDirPath.substring(0, index)
|
||||
let savePath = context.filesDir + cacheDirPath
|
||||
let res = fs.accessSync(dirPath)
|
||||
if (!res) {
|
||||
fs.mkdirSync(dirPath, true)
|
||||
}
|
||||
return savePath
|
||||
} catch (error) {
|
||||
let e: BusinessError = error as BusinessError;
|
||||
HXLog.e(FileManager.TAG, `error code + ${e.code}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export const TITLE_BAR_HEIGHT = 50
|
||||
@@ -0,0 +1,36 @@
|
||||
import { AppUtil } from "@pura/harmony-utils"
|
||||
|
||||
export class FirstPageDebugUtil {
|
||||
/**
|
||||
* 九宫格全部节点
|
||||
*/
|
||||
static isAllGridNodeUseTestUrl: boolean = false
|
||||
|
||||
/**
|
||||
* 九宫格页面替换跳转页面
|
||||
* m/margins/index.html => account-open/index.html
|
||||
* community/m/home.html => 测试环境futures-test.10jqka.com.cn/community/m/home.html
|
||||
*/
|
||||
static isGridNodeAllPageChangeUrl: boolean = false
|
||||
|
||||
/**
|
||||
* 市场排名推荐合约接口
|
||||
*/
|
||||
static isMarketRankingUseTestUrl: boolean = false
|
||||
|
||||
static getGridNodeUrl(isTest: boolean): string {
|
||||
if (isTest) {
|
||||
return AppUtil.getContext().resourceManager.getStringSync($r('app.string.all_gride_node_url_test'))
|
||||
}
|
||||
return AppUtil.getContext().resourceManager.getStringSync($r('app.string.all_gride_node_url'))
|
||||
}
|
||||
|
||||
static getMarketRankingRecommendUrl(isTest: boolean): string {
|
||||
if (isTest) {
|
||||
return AppUtil.getContext().resourceManager
|
||||
.getStringSync($r('app.string.market_ranking_recommend_url_test'))
|
||||
}
|
||||
return AppUtil.getContext().resourceManager
|
||||
.getStringSync($r('app.string.market_ranking_recommend_url'))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AppConfigManager } from 'biz_common/src/main/ets/appconfig/AppConfigManager';
|
||||
import { CookieManager } from 'biz_hxservice';
|
||||
|
||||
export function buildHeader() : Record<string, Object> {
|
||||
let header : Record<string, Object> = {
|
||||
'User-Agent': AppConfigManager.getInstance().getUA(),
|
||||
'Content-Type': 'application/json',
|
||||
'Connection': 'keep-alive',
|
||||
}
|
||||
// 接入Cookie,已登录用户可获取完整数据
|
||||
const cookie = CookieManager.getInstance().getCookieSync()
|
||||
if (cookie) {
|
||||
header['Cookie'] = cookie
|
||||
}
|
||||
return header;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { image } from "@kit.ImageKit";
|
||||
import { http } from "@kit.NetworkKit";
|
||||
import { HXLog } from "biz_common";
|
||||
import { BusinessError } from "@kit.BasicServicesKit";
|
||||
|
||||
/**
|
||||
* author : liuqingliang@myhexin.com
|
||||
* time : created on 2026/6/1
|
||||
* desc : 网络图片加载
|
||||
*/
|
||||
const TAG = 'ImageLoader'
|
||||
|
||||
export class ImageLoader {
|
||||
static load(imgUrl: string, onLoadFailed: (errMsg: string) => void, onResourceReady: (img: image.PixelMap) => void) {
|
||||
try {
|
||||
const httpRequest = http.createHttp()
|
||||
httpRequest.request(imgUrl).then(async (response) => {
|
||||
if (response.responseCode === 200) {
|
||||
const arrayBuffer = response.result as ArrayBuffer
|
||||
const imageSource = image.createImageSource(arrayBuffer)
|
||||
try {
|
||||
let img = imageSource.createPixelMapSync()
|
||||
onResourceReady(img)
|
||||
} finally {
|
||||
await imageSource.release()
|
||||
}
|
||||
} else {
|
||||
onLoadFailed(`ImageLoader load request failed. responseCode:${response.responseCode}`)
|
||||
}
|
||||
}).catch((err: Error) => {
|
||||
onLoadFailed(err.message)
|
||||
HXLog.e(TAG, JSON.stringify(err))
|
||||
}).finally(() => {
|
||||
httpRequest.destroy()
|
||||
})
|
||||
} catch (e) {
|
||||
const err = e as BusinessError
|
||||
HXLog.e(TAG, `ImageLoader load failed. msg:${err.message}`)
|
||||
onLoadFailed(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
static async loadSync(imgUrl: string): Promise<image.PixelMap | null> {
|
||||
let img: image.PixelMap | null = null
|
||||
let imageSource: image.ImageSource | null = null
|
||||
try {
|
||||
const httpRequest = http.createHttp()
|
||||
try {
|
||||
const response: http.HttpResponse = await httpRequest.request(imgUrl)
|
||||
if (response.responseCode === 200) {
|
||||
const arrayBuffer = response.result as ArrayBuffer
|
||||
imageSource = image.createImageSource(arrayBuffer)
|
||||
img = imageSource.createPixelMapSync()
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as BusinessError
|
||||
HXLog.e(TAG, `ImageLoader load failed. msg:${err.message}`)
|
||||
} finally {
|
||||
httpRequest.destroy()
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as BusinessError
|
||||
HXLog.e(TAG, `ImageLoader load failed. msg:${err.message}`)
|
||||
} finally {
|
||||
if (imageSource) {
|
||||
imageSource.release()
|
||||
}
|
||||
}
|
||||
return img
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* author:zhangshan2
|
||||
* created on 2024/11/22
|
||||
* desc:处理资源工具类
|
||||
*/
|
||||
export function getResourceString(context: Context, resource: Resource): string {
|
||||
return context.resourceManager.getStringSync(resource)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { HXLog, jumpToWebPage } from 'biz_common'
|
||||
import { StringUtils } from 'hxutil'
|
||||
import { landingPageRouteParam } from '../route'
|
||||
import { promptAction } from '@kit.ArkUI';
|
||||
import { call } from '@kit.TelephonyKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
|
||||
export class RouterUtil {
|
||||
static jumpPage(url: string | null, fullscreen?: boolean) {
|
||||
if (url && StringUtils.isNotEmpty(url)) {
|
||||
if (url.startsWith('client.html')) {
|
||||
let protocolItems = url.split('^')
|
||||
for (let index = 0; index < protocolItems.length; index++) {
|
||||
const protocolItem = protocolItems[index]
|
||||
let urlKey = 'url'
|
||||
if (protocolItem.startsWith(urlKey)) {
|
||||
let jumpUrl = protocolItem.substring(urlKey.length + 1)
|
||||
if (jumpUrl.startsWith('http://') || jumpUrl.startsWith('https://')) {
|
||||
jumpToWebPage(landingPageRouteParam(url, jumpUrl, fullscreen))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
promptAction.showToast({ message: '鸿蒙暂不支持' })
|
||||
} else if (url.startsWith('tel://')) {
|
||||
call.makeCall(url).then(() => {
|
||||
HXLog.i("Telephony Service", "makeCallSuccess");
|
||||
}).catch((err: BusinessError) => {
|
||||
HXLog.i("Telephony Service", "makeCallFailed with message " + err.message)
|
||||
});
|
||||
} else {
|
||||
jumpToWebPage(landingPageRouteParam(url, url, fullscreen))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user