fix(ai-pick): 修复 cardData 初始化与列表分隔线
This commit is contained in:
@@ -3,20 +3,14 @@ import { AiPickModel } from './AiPickModel';
|
||||
import { cellColor, cellValue, formatColName } from './AiPickUtils';
|
||||
import { MAX_ITEMS } from './AiPickConstant';
|
||||
|
||||
// @Builder 按引用传递要求单参数 + 对象字面量,用此 interface 包装 tabIdx
|
||||
interface StrategyDescParams {
|
||||
tabIdx: number;
|
||||
}
|
||||
|
||||
@Component
|
||||
export struct AiPick {
|
||||
// 对应 Vue 端 cardData prop:从第一层 floorList 透传下来的单卡片配置
|
||||
@Prop cardData: CardData = {
|
||||
card_id: 0,
|
||||
card_key: 'placeholder',
|
||||
card_title: { value: '' },
|
||||
bury_point: '',
|
||||
};
|
||||
// cardData prop:从第一层 floorList 透传下来的单卡片配置
|
||||
@Watch('onCardDataChanged') @Prop cardData: CardData | undefined;
|
||||
// 初值用 placeholder cardData 占位(仅类型占位,实际值由 aboutToAppear 用真实 cardData 创建)
|
||||
@State vm: AiPickModel = new AiPickModel(this.cardData);
|
||||
@State tabIdx: number = 0;
|
||||
@@ -26,20 +20,16 @@ export struct AiPick {
|
||||
this.vm.loadData();
|
||||
}
|
||||
|
||||
// 对应 Vue 端 watch(() => props.cardData, () => cardHandler.value.setCardData(...))
|
||||
// 父组件传入不同卡片配置时,重新构造 Model 并重新加载数据
|
||||
@Watch('cardData')
|
||||
onCardDataChanged(): void {
|
||||
this.vm = new AiPickModel(this.cardData);
|
||||
this.tabIdx = 0;
|
||||
this.vm.loadData();
|
||||
}
|
||||
|
||||
// 按引用传递:单参数对象字面量,$$.tabIdx 变化时内部 UI 自动刷新
|
||||
@Builder
|
||||
StrategyDesc($$: StrategyDescParams) {
|
||||
// 图片:imgs[0] 白天,imgs[1] 夜间;对应 Vue 端 desc-text.vue 的 imgSrc computed
|
||||
// 外层整体点击跳 jumpToDetail2,tag 点击阻止冒泡后跳 jumpToLabel(对应 @click.stop)
|
||||
// 图片:imgs[0] 白天,imgs[1] 夜间
|
||||
Row() {
|
||||
// 左侧:问句 + 标签
|
||||
Column({ space: 8 }) {
|
||||
@@ -76,8 +66,7 @@ export struct AiPick {
|
||||
.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
|
||||
// 右侧:策略图片(仅系统策略有图,对应 Vue 端 desc-text.vue 右侧 image)
|
||||
// 对应 Vue 端 v-if="isTrueArray(tabInfo.imgs) && imgSrc":
|
||||
// 右侧:策略图片(仅系统策略有图,右侧 image)
|
||||
// imgs 长度>0 且当前选中位(imgs[0],暂无主题切换)非空才渲染
|
||||
if (this.vm.tabs[$$.tabIdx].strategy.imgs.length > 0 && this.vm.tabs[$$.tabIdx].strategy.imgs[0]) {
|
||||
Image(this.vm.tabs[$$.tabIdx].strategy.imgs[0])
|
||||
@@ -129,9 +118,6 @@ export struct AiPick {
|
||||
MainContent() {
|
||||
Column() {
|
||||
// ── 标题栏 ──────────────────────────────────────────────
|
||||
// 对应 Vue 端 card-title 组件:点击跳转 cardData.card_url(卡片配置的通用链接),
|
||||
// 与"一句话定制策略"(jumpToNewHome)无关;mockFloorData 中 ai-pick 的 card_url 为空,
|
||||
// 故标题栏本身不绑定跳转行为,也不显示右箭头
|
||||
Row() {
|
||||
Text('AI选期')
|
||||
.fontSize(18)
|
||||
@@ -245,9 +231,7 @@ export struct AiPick {
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 12, bottom: 12 })
|
||||
.border(index === Math.min(MAX_ITEMS, this.vm.tabs[this.tabIdx].items.length) - 1
|
||||
? undefined
|
||||
: { width: { bottom: 0.5 }, color: $r('app.color.divider_color') })
|
||||
.border({ width: { bottom: 0.5 }, color: $r('app.color.divider_color') })
|
||||
.onClick(() => {
|
||||
this.vm.jumpToDetail(item.contract, item.market);
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// AI选期接口层:负责从网络获取原始数据并解析为视图数据
|
||||
// 对应 Vue 端 index.vue 里的 getData()(http() 封装 + 解析流程)
|
||||
// 目前网络请求未接入,直接返回 Mock 数据;接入时替换 fetchAiPickTabs 内部实现即可。
|
||||
import {
|
||||
CardData,
|
||||
@@ -14,10 +13,10 @@ import { getResData, getTableList, ResData } from './AiPickUtils';
|
||||
import { MOCK_RAW_TABS } from './AiPickMock';
|
||||
import { MAX_ITEMS, MAX_TABS, MAX_TAGS } from './AiPickConstant';
|
||||
|
||||
// 新版接口地址(isCustomVersion=true):
|
||||
// {API_HOST}futgwapi/api/ai_diagnosis/home_strategy/v1/user_data
|
||||
// 旧版接口地址(isCustomVersion=false):
|
||||
// {API_HOST}futgwapi/api/ai_diagnosis/system_strategy/v1/home_data
|
||||
/**
|
||||
* 新版接口地址(isCustomVersion=true):{API_HOST}futgwapi/api/ai_diagnosis/home_strategy/v1/user_data
|
||||
* 旧版接口地址(isCustomVersion=false):{API_HOST}futgwapi/api/ai_diagnosis/system_strategy/v1/home_data
|
||||
**/
|
||||
|
||||
// 模拟网络延迟(毫秒),验证 loading 状态和异步流程用;接入真实接口后删除
|
||||
const MOCK_DELAY_MS = 300;
|
||||
@@ -53,12 +52,9 @@ export function parseRawTab(item: RawPickTabItem): PickTabData {
|
||||
const strategy: PickStrategy = {
|
||||
id: strategyDetail.id,
|
||||
name: strategyDetail.strategy_name,
|
||||
// 对应 Vue 端 user_question || content:空字符串也会 fallback
|
||||
detail: strategyDetail.user_question || strategyDetail.content || '',
|
||||
// 对应 Vue 端 getDefaultValue(strategy_type, 'custom_strategy')
|
||||
type: strategyDetail.strategy_type || 'custom_strategy',
|
||||
periodType: item.type,
|
||||
// 对应 Vue 端默认值 ['自编'](非空数组不会被替换,因为空数组本身是 truthy)
|
||||
tagList: (strategyDetail.tag_list ?? ['自编']).slice(0, MAX_TAGS),
|
||||
imgs,
|
||||
count,
|
||||
@@ -82,8 +78,7 @@ export function parseRawTabs(rawTabs: RawPickTabItem[]): PickTabData[] {
|
||||
// 获取 AI 选期视图数据(对应 Vue 端 getData() + useCardHandler.showData 决策)
|
||||
// data_completed=true:使用 mod_data[0].data 内嵌数据,不发请求
|
||||
// data_completed=false:发起 HTTP 请求(当前走 Mock),并通过 AiPickStorage 写入卡片级缓存
|
||||
// TODO: 接入真实接口后,改为调用 @ohos.net.http 请求 cardData.mod_data[0].url,
|
||||
// 解析 JSON 响应体为 RawPickTabItem[] 后传入 parseRawTabs
|
||||
// TODO: 接入真实接口后,改为调用 @ohos.net.http 请求 cardData.mod_data[0].url,解析 JSON 响应体为 RawPickTabItem[] 后传入 parseRawTabs
|
||||
export function fetchAiPickTabs(cardData: CardData): Promise<PickTabData[]> {
|
||||
return new Promise<PickTabData[]>((resolve) => {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
// AI选期卡片常量配置,供 AiPickModel / AiPickUtils / AiPick 共用
|
||||
|
||||
// 最多展示的策略 Tab 数(对应 Vue 端 getData 里 .slice(0, 5))
|
||||
// 最多展示的策略 Tab 数
|
||||
export const MAX_TABS = 5;
|
||||
|
||||
// 每个 Tab 最多展示的合约条数(对应 Vue 端 tabDataList 里 .slice(0, 3))
|
||||
// 每个 Tab 最多展示的合约条数
|
||||
export const MAX_ITEMS = 3;
|
||||
|
||||
// 策略标签最多展示条数(对应 Vue 端 tagList.slice(0, 3))
|
||||
// 策略标签最多展示条数
|
||||
export const MAX_TAGS = 3;
|
||||
|
||||
// 表格列数:合约名称 + 最多 2 个数据列(对应 Vue 端 getShowKeyList 的 slice(0, 3))
|
||||
// 表格列数:合约名称 + 最多 2 个数据列
|
||||
export const MAX_SHOW_KEYS = 3;
|
||||
|
||||
// 自定义 DOUBLE 字段最多取前 2 个用于表头(对应 Vue 端 showKey = doubleListKey.slice(0, 2))
|
||||
// 自定义 DOUBLE 字段最多取前 2 个用于表头
|
||||
export const MAX_DOUBLE_KEYS = 2;
|
||||
|
||||
// 字段黑名单:过滤 field_list 中不应展示的字段(对应 Vue 端 utils.ts 的 blackList)
|
||||
// 字段黑名单:过滤 field_list 中不应展示的字段
|
||||
export const BLACK_LIST: string[] = [
|
||||
'合约代码', '合约简称', '收盘价', '最新价', '涨跌幅', 'code',
|
||||
'market_id', 'market_code', '最新涨跌幅',
|
||||
'合约代码',
|
||||
'合约简称',
|
||||
'收盘价',
|
||||
'最新价',
|
||||
'涨跌幅',
|
||||
'code',
|
||||
'market_id',
|
||||
'market_code',
|
||||
'最新涨跌幅',
|
||||
];
|
||||
|
||||
@@ -5,6 +5,14 @@ import { fetchAiPickTabs, parseRawTabs } from './AiPickApi';
|
||||
// 顶层缓存 key:对应 Vue 端 __GLOBAL__.cardsDataCache(跨组件/跨页面共享)
|
||||
const APP_STORAGE_CACHE_KEY = 'cardsDataCache';
|
||||
|
||||
// 空 CardData 占位(仅用于锺型调用场景,实际使用需设 cardData 后重新构造 Model)
|
||||
const EMPTY_CARD_DATA: CardData = {
|
||||
card_id: 0,
|
||||
card_key: '',
|
||||
card_title: { value: '' },
|
||||
bury_point: '',
|
||||
};
|
||||
|
||||
@Observed
|
||||
export class AiPickModel {
|
||||
cardData: CardData;
|
||||
@@ -20,9 +28,11 @@ export class AiPickModel {
|
||||
// 1. data_completed=true 且 mod_data[0].data 内嵌 → 直接解析内嵌数据(无需请求)
|
||||
// 2. 否则从 AppStorage 卡片级缓存读出(对应 __GLOBAL__.cardsDataCache)
|
||||
// 3. 都没有 → 空数组,View 层展示 loading;后续 loadData() 异步拉取
|
||||
constructor(cardData: CardData) {
|
||||
this.cardData = cardData;
|
||||
this.cardKey = cardData.card_key;
|
||||
// cardData 可选:不传则使用空占位,Model 处于“未绑定”状态,
|
||||
// 需在 View 层 aboutToAppear 后赋值真实的 cardData(参见 AiPick.ets 的 @Watch 逻辑)
|
||||
constructor(cardData?: CardData) {
|
||||
this.cardData = cardData ?? EMPTY_CARD_DATA;
|
||||
this.cardKey = this.cardData.card_key;
|
||||
this.tabs = this.computeShowData();
|
||||
this.dataReady = this.cardData.data_completed === true && this.tabs.length > 0;
|
||||
}
|
||||
@@ -41,8 +51,13 @@ export class AiPickModel {
|
||||
|
||||
// 拉取数据:
|
||||
// - data_completed=true:直接完成,不再发请求
|
||||
// - 未绑定 cardData:不动作
|
||||
// - 其他:调用 fetchAiPickTabs,请求成功后写入 AppStorage
|
||||
async loadData(): Promise<void> {
|
||||
if (this.cardKey === '') {
|
||||
// 未绑定 cardData,不动作
|
||||
return;
|
||||
}
|
||||
if (this.cardData.data_completed === true) {
|
||||
this.dataReady = true;
|
||||
this.dataError = false;
|
||||
|
||||
Reference in New Issue
Block a user