feat(cards): 完善 AI Pick 样式和深色主题
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
EMPTY_IMAGE_LIGHT,
|
||||
INFO_IMAGE_DARK,
|
||||
INFO_IMAGE_LIGHT,
|
||||
MAX_ITEMS,
|
||||
RIGHT_ARROW_DARK,
|
||||
RIGHT_ARROW_LIGHT,
|
||||
TITLE_ARROW_DARK,
|
||||
@@ -29,6 +30,18 @@ interface StrategyDescParams {
|
||||
tabIdx: number;
|
||||
}
|
||||
|
||||
const CARD_TITLE_HEIGHT = 48;
|
||||
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 CARD_CONTENT_HEIGHT = CARD_TITLE_HEIGHT + MAIN_CONTENT_HEIGHT;
|
||||
const NO_CONTRACT_HEIGHT = STRATEGY_DESC_HEIGHT + TABLE_HEIGHT;
|
||||
|
||||
@Component
|
||||
export struct AiPickNodeComponent {
|
||||
// 当前卡片配置。
|
||||
@@ -142,31 +155,32 @@ export struct AiPickNodeComponent {
|
||||
|
||||
@Builder
|
||||
CardTitle() {
|
||||
Row({ space: 4 }) {
|
||||
Row() {
|
||||
Text(this.cardConfig?.card_title.value || $r('app.string.ai_pick_title'))
|
||||
.fontSize(18)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.fontWeight(500)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
.maxLines(1)
|
||||
|
||||
if ((this.cardConfig?.card_url.android ?? '') !== '' ||
|
||||
(this.cardConfig?.card_url.ios ?? '') !== '') {
|
||||
Image(this.isDarkMode ? TITLE_ARROW_DARK : TITLE_ARROW_LIGHT)
|
||||
.width(12)
|
||||
.height(12)
|
||||
.width(20)
|
||||
.height(20)
|
||||
}
|
||||
|
||||
if ((this.cardConfig?.explain_message ?? '') !== '') {
|
||||
Image(this.isDarkMode ? INFO_IMAGE_DARK : INFO_IMAGE_LIGHT)
|
||||
.width(16)
|
||||
.height(16)
|
||||
.margin({ left: 8 })
|
||||
.onClick((event: ClickEvent) => {
|
||||
this.showExplainSheet = true;
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height(48)
|
||||
.height(CARD_TITLE_HEIGHT)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.onClick(() => {
|
||||
this.jumpToCardTitle();
|
||||
@@ -232,16 +246,18 @@ export struct AiPickNodeComponent {
|
||||
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.text_blue'))
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Row() {
|
||||
Text(tag)
|
||||
.fontSize(11)
|
||||
.fontColor($r('app.color.text_blue'))
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
.height(18)
|
||||
.padding({ left: 3, right: 3 })
|
||||
.borderRadius(2)
|
||||
.border({ width: 0.5, color: $r('app.color.text_blue') })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.onClick(() => {
|
||||
this.jumpToLabel(
|
||||
this.cardData.tabs[$$.tabIdx].strategy,
|
||||
@@ -255,7 +271,6 @@ export struct AiPickNodeComponent {
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.margin({ bottom: 10 })
|
||||
|
||||
// 右侧:策略图片(仅系统策略有图,右侧 image)
|
||||
// imgs 长度>0 且当前选中位(imgs[0],暂无主题切换)非空才渲染
|
||||
@@ -268,7 +283,8 @@ export struct AiPickNodeComponent {
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.margin({ top: 10 })
|
||||
.height(STRATEGY_DESC_HEIGHT)
|
||||
.padding({ top: 10, bottom: 10 })
|
||||
.alignItems(VerticalAlign.Top)
|
||||
.onClick(() => {
|
||||
this.jumpToStrategyDetail(
|
||||
@@ -285,11 +301,11 @@ export struct AiPickNodeComponent {
|
||||
|
||||
Column() {
|
||||
Text($r('app.string.ai_pick_loading'))
|
||||
.fontSize(16)
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_grey'))
|
||||
}
|
||||
.width('100%')
|
||||
.height(240)
|
||||
.height(MAIN_CONTENT_HEIGHT)
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@@ -297,6 +313,9 @@ export struct AiPickNodeComponent {
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor($r('app.color.card_bg'))
|
||||
.borderRadius(4)
|
||||
.padding({ left: 10, right: 10, bottom: 10 })
|
||||
.bindSheet(this.showExplainSheet, this.ExplainSheet, {
|
||||
height: SheetSize.FIT_CONTENT,
|
||||
dragBar: true,
|
||||
@@ -325,14 +344,15 @@ export struct AiPickNodeComponent {
|
||||
ForEach(this.cardData.tabs, (tab: PickTabData, index: number) => {
|
||||
Text(`${tab.strategy.name}(${tab.strategy.count})`)
|
||||
.fontSize(14)
|
||||
.fontWeight(this.tabIdx === index ? FontWeight.Bold : FontWeight.Normal)
|
||||
.fontWeight(this.tabIdx === index ? FontWeight.Medium : FontWeight.Normal)
|
||||
.fontColor(this.tabIdx === index
|
||||
? $r('app.color.text_blue')
|
||||
: $r('app.color.text_secondary'))
|
||||
.constraintSize({ minWidth: 63 })
|
||||
.constraintSize({ minWidth: 66 })
|
||||
.maxLines(1)
|
||||
.textAlign(TextAlign.Center)
|
||||
.padding({ top: 6, bottom: 6, left: 12, right: 12 })
|
||||
.height(TAB_BAR_HEIGHT)
|
||||
.padding({ left: 8, right: 8 })
|
||||
.borderRadius(4)
|
||||
.backgroundColor(this.tabIdx === index
|
||||
? $r('app.color.pill_active_bg')
|
||||
@@ -347,7 +367,8 @@ export struct AiPickNodeComponent {
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height(30)
|
||||
.height(TAB_BAR_HEIGHT)
|
||||
.padding({ right: 10 })
|
||||
.scrollable(ScrollDirection.Horizontal)
|
||||
.scrollBar(BarState.Off)
|
||||
.align(Alignment.Start)
|
||||
@@ -358,16 +379,17 @@ export struct AiPickNodeComponent {
|
||||
if (this.showTabGradient()) {
|
||||
Row()
|
||||
.width(24)
|
||||
.height(30)
|
||||
.height(TAB_BAR_HEIGHT)
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [['#00FFFFFF', 0], [$r('app.color.card_bg'), 1]],
|
||||
colors: [[$r('app.color.tab_gradient_start'), 0],
|
||||
[$r('app.color.card_bg'), 1]],
|
||||
})
|
||||
.hitTestBehavior(HitTestMode.None)
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height(30)
|
||||
.height(TAB_BAR_HEIGHT)
|
||||
.onAreaChange((oldValue: Area, newValue: Area) => {
|
||||
this.tabViewportWidth = Number(newValue.width);
|
||||
})
|
||||
@@ -380,97 +402,102 @@ export struct AiPickNodeComponent {
|
||||
if (this.cardData.tabs[this.tabIdx].items.length === 0) {
|
||||
this.EmptyState($r('app.string.ai_pick_no_contract'))
|
||||
} else {
|
||||
// ── 表头 ──────────────────────────────────────────────
|
||||
Row() {
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[0]))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.width('35%')
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[1]))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.layoutWeight(1)
|
||||
.margin({ left: 8 })
|
||||
.textAlign(TextAlign.End)
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[2]))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.layoutWeight(1)
|
||||
.margin({ left: 8 })
|
||||
.textAlign(TextAlign.End)
|
||||
}
|
||||
.width('100%')
|
||||
.height(24)
|
||||
.margin({ top: 8 })
|
||||
.padding({ bottom: 2 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.border({ width: { bottom: 0.5 }, color: $r('app.color.divider_color') })
|
||||
Column() {
|
||||
// ── 表头 ────────────────────────────────────────────
|
||||
Row({ space: 8 }) {
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[0]))
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.width('35%')
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[1]))
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.layoutWeight(1)
|
||||
.textAlign(TextAlign.End)
|
||||
Text(formatColName(this.cardData.tabs[this.tabIdx].keysList[2]))
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_tertiary'))
|
||||
.layoutWeight(1)
|
||||
.textAlign(TextAlign.End)
|
||||
}
|
||||
.width('100%')
|
||||
.height(TABLE_HEADER_HEIGHT)
|
||||
|
||||
// ── 合约列表 ──────────────────────────────────────────
|
||||
ForEach(
|
||||
this.cardData.tabs[this.tabIdx].items,
|
||||
(item: PickContractItem) => {
|
||||
Row() {
|
||||
Row({ space: 5 }) {
|
||||
Text(item.name)
|
||||
// ── 合约列表 ────────────────────────────────────────
|
||||
ForEach(
|
||||
this.cardData.tabs[this.tabIdx].items,
|
||||
(item: PickContractItem) => {
|
||||
Row({ space: 8 }) {
|
||||
Row({ space: 5 }) {
|
||||
Text(item.name)
|
||||
.fontSize(16)
|
||||
.maxFontSize(16)
|
||||
.minFontSize(9)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
.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.color_orange'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.textAlign(TextAlign.Center)
|
||||
.borderRadius(2)
|
||||
.backgroundColor($r('app.color.bg_orange'))
|
||||
}
|
||||
}
|
||||
.width('35%')
|
||||
.alignItems(VerticalAlign.Center)
|
||||
|
||||
Text(cellValue(
|
||||
this.cardData.tabs[this.tabIdx].keysList[1],
|
||||
item,
|
||||
this.cardData.tabs[this.tabIdx].fieldList,
|
||||
))
|
||||
.fontSize(16)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
.maxFontSize(16)
|
||||
.minFontSize(9)
|
||||
.fontWeight(500)
|
||||
.fontFamily('monospace')
|
||||
.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)
|
||||
.fontFamily('monospace')
|
||||
.fontColor(cellColor(this.cardData.tabs[this.tabIdx].keysList[2], item))
|
||||
.layoutWeight(1)
|
||||
.textAlign(TextAlign.End)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
if (item.isMain) {
|
||||
Text($r('app.string.ai_pick_main_contract'))
|
||||
.fontSize(11)
|
||||
.fontColor($r('app.color.color_orange'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.textAlign(TextAlign.Center)
|
||||
.borderRadius(2)
|
||||
.backgroundColor($r('app.color.bg_orange'))
|
||||
}
|
||||
}
|
||||
.width('35%')
|
||||
.width('100%')
|
||||
.height(TABLE_ROW_HEIGHT)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
|
||||
Text(cellValue(
|
||||
this.cardData.tabs[this.tabIdx].keysList[1],
|
||||
item,
|
||||
this.cardData.tabs[this.tabIdx].fieldList,
|
||||
))
|
||||
.fontSize(16)
|
||||
.fontFamily('monospace')
|
||||
.fontColor(cellColor(this.cardData.tabs[this.tabIdx].keysList[1], item))
|
||||
.layoutWeight(1)
|
||||
.margin({ left: 8 })
|
||||
.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)
|
||||
.fontFamily('monospace')
|
||||
.fontColor(cellColor(this.cardData.tabs[this.tabIdx].keysList[2], item))
|
||||
.layoutWeight(1)
|
||||
.margin({ left: 8 })
|
||||
.textAlign(TextAlign.End)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
.width('100%')
|
||||
.height(44)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.border({ width: { bottom: 0.5 }, color: $r('app.color.divider_color') })
|
||||
.onClick(() => {
|
||||
this.jumpToDetail(item);
|
||||
})
|
||||
},
|
||||
// 行情字段参与 key,确保 price/priceChg 变化后对应列表行重新渲染。
|
||||
(item: PickContractItem) =>
|
||||
`${item.contract}_${item.market}_${item.price ?? ''}_${item.priceChg ?? ''}`,
|
||||
)
|
||||
.border({ width: { top: 1 }, color: $r('app.color.divider_color') })
|
||||
.onClick(() => {
|
||||
this.jumpToDetail(item);
|
||||
})
|
||||
},
|
||||
// 行情字段参与 key,确保整体替换 CardData 后列表行同步刷新。
|
||||
(item: PickContractItem) =>
|
||||
`${item.contract}_${item.market}_${item.price ?? ''}_${item.priceChg ?? ''}`,
|
||||
)
|
||||
}
|
||||
.width('100%')
|
||||
.height(TABLE_HEIGHT)
|
||||
}
|
||||
|
||||
// ── 一句话定制策略 ──────────────────────────────────────
|
||||
@@ -484,30 +511,30 @@ export struct AiPickNodeComponent {
|
||||
.height(14)
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
.width('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.margin({ top: 6 })
|
||||
.onClick(() => {
|
||||
this.jumpToNewHome();
|
||||
})
|
||||
.width('100%')
|
||||
.height(CUSTOM_STRATEGY_HEIGHT)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.onClick(() => {
|
||||
this.jumpToNewHome();
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height(CARD_CONTENT_HEIGHT)
|
||||
}
|
||||
|
||||
@Builder
|
||||
EmptyState(message: ResourceStr) {
|
||||
Column() {
|
||||
Image(this.isDarkMode ? EMPTY_IMAGE_DARK : EMPTY_IMAGE_LIGHT)
|
||||
.width(120)
|
||||
.height(120)
|
||||
.margin({ bottom: 8 })
|
||||
.width(100)
|
||||
.height(100)
|
||||
Text(message)
|
||||
.fontSize(16)
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_grey'))
|
||||
}
|
||||
.width('100%')
|
||||
.height(240)
|
||||
.height(NO_CONTRACT_HEIGHT)
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,37 @@
|
||||
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
|
||||
import { AbilityConstant, Configuration, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
|
||||
import { resourceManager } from '@kit.LocalizationKit';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { window } from '@kit.ArkUI';
|
||||
|
||||
const DOMAIN = 0x0000;
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
private updateDarkMode(isDarkMode: boolean): void {
|
||||
AppStorage.setOrCreate<boolean>('enableDarkMode', isDarkMode);
|
||||
}
|
||||
|
||||
private syncDarkModeFromResources(): void {
|
||||
const configuration: resourceManager.Configuration =
|
||||
this.context.resourceManager.getConfigurationSync();
|
||||
this.updateDarkMode(configuration.colorMode === resourceManager.ColorMode.DARK);
|
||||
}
|
||||
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
|
||||
try {
|
||||
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
|
||||
} catch (err) {
|
||||
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
|
||||
}
|
||||
this.syncDarkModeFromResources();
|
||||
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
}
|
||||
|
||||
onConfigurationUpdate(newConfig: Configuration): void {
|
||||
if (newConfig.colorMode !== undefined) {
|
||||
this.updateDarkMode(newConfig.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
|
||||
}
|
||||
@@ -37,7 +55,7 @@ export default class EntryAbility extends UIAbility {
|
||||
}
|
||||
|
||||
onForeground(): void {
|
||||
// Ability has brought to foreground
|
||||
this.syncDarkModeFromResources();
|
||||
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
|
||||
}
|
||||
|
||||
@@ -45,4 +63,4 @@ export default class EntryAbility extends UIAbility {
|
||||
// Ability has back to background
|
||||
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ export struct MarketRankingNodeComponent {
|
||||
.height(30)
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [[$r('app.color.transparent_white'), 0], [$r('app.color.card_bg'), 1]],
|
||||
colors: [[$r('app.color.tab_gradient_start'), 0], [$r('app.color.card_bg'), 1]],
|
||||
})
|
||||
.hitTestBehavior(HitTestMode.None)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
{ "name": "color_fall", "value": "#07AB4B" },
|
||||
{ "name": "color_orange", "value": "#FF661A" },
|
||||
{ "name": "bg_orange", "value": "#1AFF661A" },
|
||||
{ "name": "transparent_white", "value": "#00FFFFFF" }
|
||||
{ "name": "transparent_white", "value": "#00FFFFFF" },
|
||||
{ "name": "tab_gradient_start", "value": "#00FFFFFF" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{ "name": "pill_active_bg", "value": "#1A527DFF" },
|
||||
{ "name": "pill_inactive_bg", "value": "#0FFFFFFF" },
|
||||
{ "name": "color_rise", "value": "#FF2436" },
|
||||
{ "name": "color_fall", "value": "#07AB4B" }
|
||||
{ "name": "color_fall", "value": "#07AB4B" },
|
||||
{ "name": "tab_gradient_start", "value": "#002B2B2B" }
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user