HarmonyOS 6.1 实战:弹窗的报价计算由fixQuote()方法根据chipA和chipB的组合动态返回,体现了状态驱动的业务逻辑计算模式
引言

经典老爷车收藏是一个融合了历史情怀、机械美学和投资价值的小众但高净值垂直领域。从1965年的大众甲壳虫到1990年的保时捷911 964,每一台经典车都承载着特定时代的设计语言与工业记忆。将这样一个需要兼顾专业鉴定信息、修复工程管理、配件交易和藏家社区社交的复杂业务场景,转化为一个移动应用页面,对开发者的数据建模能力、信息架构设计能力和交互流程编排能力都提出了极高的要求。本文将深入解析一款基于HarmonyOS ArkTS声明式UI框架构建的复古经典车馆应用页面,该页面以复古红、黄铜金、米白和墨绿四色构建了浓郁的怀旧视觉基调。
从技术架构层面分析,该应用采用HarmonyOS ArkTS的@Entry+@Component标准入口组件结构,通过@State装饰器管理十余个响应式状态变量。应用以统一的GoodsItem接口描述六类业务数据——经典车展、老爷车市、修复工坊、配件集市、车友会和消息通知——共78条数据记录。页面以七Tab导航为核心框架,配合五种弹窗(发布藏车、修复评估、配件求购、删除确认、车辆详情)实现完整的"浏览-鉴定-修复-求购-收藏"业务闭环。整个应用以单文件结构完成所有功能逻辑和UI渲染,体现了ArkTS声明式编程在中等复杂度业务场景下的适用性。
在视觉设计层面,应用采用了复古红(#8D2F2F)作为主品牌色,黄铜金(#A1887F)作为辅助色和装饰色,米白(#FAF3E8)作为浅色背景和文字色,墨绿(#3E5641)作为修复工坊模块的主题色,警示红(#E53935)作为价格和警示信息色。这套配色方案精准地还原了经典车时代的色彩记忆——复古红让人联想到上世纪的汽车广告海报,黄铜金呼应了老车的镀铬装饰件,米白如同泛黄的老照片纸张,墨绿则暗示了修复车间的工业氛围。应用在多个关键元素上使用了700毫秒无限循环的EaseInOut脉冲缩放动画,为静态的怀旧页面注入了微妙的动态呼吸感。
数据接口与状态管理

应用首先定义了全局数据接口GoodsItem,随后在主组件中声明了状态变量和多组业务数据源,构成了页面的数据基础。
interface GoodsItem {
name: string
price: number
desc: string
score: number
num: number
tag: string
hot: string
}
@Entry
@Component
struct Index {
@State currentTab: number = 0
@State showAddModal: boolean = false
@State showFixModal: boolean = false
@State showWantModal: boolean = false
@State showDeleteModal: boolean = false
@State showDetailModal: boolean = false
@State selectedItem: GoodsItem | null = null
@State chipA: number = 0
@State chipB: number = 0
@State stepNum: number = 1
@State inputDesc: string = ''
private tabs: string[] = ['经典车展', '老爷车市', '修复工坊', '配件集市', '车友会', '消息', '我的']
private eraChips: string[] = ['1950前', '1951-1970', '1971-1990', '1991-2000']
private condChips: string[] = ['原版未修', '修复翻新', '改装升级']
private fixItemChips: string[] = ['发动机大修', '内饰翻新', '车漆修复', '底盘整备']
private fixDurChips: string[] = ['1个月', '3个月', '6个月', '1年']
private partTypeChips: string[] = ['发动机件', '内饰件', '外观件', '电气件']
private wantEraChips: string[] = ['1950前', '1970前', '1990前', '2000前']
private topicChips: string[] = ['五十年代', '六十年代', '七十年代', '肌肉车', '欧陆经典', '东方记忆']
private historyChips: string[] = ['原厂档案', '两次过户', '全程养护', '老照片']
private menuList: string[] = ['我的藏车', '修复订单', '求购配件', '发布藏车', '展会门票', '账号设置']
GoodsItem接口的七个字段在不同列表中承载了差异化的业务语义。在mainList中,price为车辆价格(最高达360万元),score为稀有度百分比,num为车龄年数;在vinList中,num同样为车龄年数,tag为年代分类;在fixList中,num为工期天数,tag为修复项目类别;在partList中,num为库存件数,score为稀有度指数;在clubList中,price为点赞数,num为阅读量;在msgList中,price为关联数值,num为阅读量,hot标记未读/已读状态。这种高复用的数据接口设计在减少接口定义数量的同时,也要求开发者在渲染逻辑中清晰地处理字段语义的切换。
状态变量方面,currentTab控制Tab切换,五个show*Modal布尔值控制弹窗显隐。chipA和chipB作为通用选择索引被多个弹窗复用——发布藏车弹窗中分别记录年代选择和车况选择,修复评估弹窗中记录修复项目和期望工期,配件求购弹窗中记录配件类型和适配年代。stepNum为求购数量步进器,inputDesc绑定描述输入框文本。修复评估弹窗的报价计算由fixQuote()方法根据chipA和chipB的组合动态返回,体现了状态驱动的业务逻辑计算模式。
核心数据源:24台传世经典车

经典车展主列表是应用的核心数据资产,包含24台横跨1957年至1990年的传世经典车型。
// 经典车展主列表:24 条传世经典车
private mainList: GoodsItem[] = [
{ name: '1965 大众甲壳虫 1200', price: 128000, desc: '1965年 · 德国原版 · 手动挡', score: 96, num: 61, tag: '稀有', hot: '藏' },
{ name: '1978 奔驰 W123 230E', price: 96000, desc: '1978年 · 德系耐用典范', score: 88, num: 48, tag: '经典', hot: '热' },
{ name: '1972 宝马 2002', price: 185000, desc: '1972年 · 巴伐利亚运动轿车', score: 92, num: 54, tag: '稀有', hot: '热' },
{ name: '1963 捷豹 E-Type S1', price: 1380000, desc: '1963年 · 英伦跑车美学巅峰', score: 98, num: 63, tag: '殿堂', hot: '藏' },
{ name: '1990 保时捷 911 964', price: 890000, desc: '1990年 · 末代风冷手动挡', score: 94, num: 36, tag: '稀有', hot: '热' },
{ name: '1985 伏尔加 嘎斯24', price: 68000, desc: '1985年 · 苏联公务车代表', score: 78, num: 41, tag: '情怀', hot: '冷' },
{ name: '1965 红旗 CA72', price: 2680000, desc: '1965年 · 国礼级检阅车', score: 99, num: 61, tag: '国宝', hot: '藏' },
{ name: '1976 上海 SH760A', price: 155000, desc: '1976年 · 沪上制造记忆', score: 86, num: 50, tag: '情怀', hot: '热' },
{ name: '1969 丰田 2000GT', price: 3600000, desc: '1969年 · 东瀛第一超跑', score: 99, num: 57, tag: '殿堂', hot: '藏' },
{ name: '1961 福特 雷鸟', price: 210000, desc: '1961年 · 美式敞篷豪华', score: 84, num: 65, tag: '经典', hot: '冷' },
{ name: '1970 雪铁龙 DS21', price: 480000, desc: '1970年 · 液压悬挂传奇', score: 93, num: 56, tag: '稀有', hot: '热' },
{ name: '1980 菲亚特 124', price: 72000, desc: '1980年 · 意式家用后驱', score: 74, num: 46, tag: '经典', hot: '冷' },
{ name: '1967 阿尔法·罗密欧 Giulia', price: 520000, desc: '1967年 · 米兰弯道精灵', score: 91, num: 59, tag: '稀有', hot: '热' },
{ name: '1974 沃尔沃 P1800', price: 260000, desc: '1974年 · 北欧长寿纪录', score: 85, num: 52, tag: '经典', hot: '冷' },
{ name: '1958 凯迪拉克 Eldorado', price: 720000, desc: '1958年 · 尾翼时代巅峰', score: 95, num: 68, tag: '稀有', hot: '藏' },
{ name: '1988 宝马 M3 E30', price: 1150000, desc: '1988年 · 直六赛化机器', score: 97, num: 38, tag: '殿堂', hot: '热' },
{ name: '1969 日产 费尔雷迪 Z432', price: 980000, desc: '1969年 · 东瀛S20名机', score: 94, num: 57, tag: '稀有', hot: '藏' },
{ name: '1975 标致 504', price: 58000, desc: '1975年 · 非洲拉力常胜', score: 72, num: 51, tag: '情怀', hot: '冷' },
{ name: '1957 菲亚特 500', price: 165000, desc: '1957年 · 意大利国民小车', score: 87, num: 69, tag: '经典', hot: '热' },
{ name: '1962 奔驰 190SL', price: 880000, desc: '1962年 · 斯图加特敞篷', score: 92, num: 64, tag: '稀有', hot: '藏' },
{ name: '1979 拉达 2101', price: 38000, desc: '1979年 · 战斗民族国民车', score: 66, num: 47, tag: '情怀', hot: '冷' },
{ name: '1984 北京 BJ212', price: 46000, desc: '1984年 · 国产越野活化石', score: 76, num: 42, tag: '情怀', hot: '热' },
{ name: '1968 庞蒂亚克 GTO', price: 960000, desc: '1968年 · 肌肉车鼻祖', score: 95, num: 58, tag: '殿堂', hot: '藏' },
{ name: '1986 丰田 FJ60', price: 235000, desc: '1986年 · 方盒子越野经典', score: 83, num: 40, tag: '经典', hot: '热' }
]
24台经典车覆盖了德系(大众甲壳虫、奔驰W123/190SL、宝马2002/M3 E30)、英系(捷豹E-Type、沃克斯豪尔Victor)、意系(菲亚特124/500、阿尔法·罗密欧Giulia、标致504)、美系(福特雷鸟、凯迪拉克Eldorado、庞蒂亚克GTO)、日系(丰田2000GT、日产Z432、丰田FJ60)、苏系(伏尔加嘎斯24、拉达2101)和国产(红旗CA72、上海SH760A、北京BJ212)七大车系,价格从3.8万元的拉达2101到360万元的丰田2000GT,稀有度评分从66到99分。tag字段使用"稀有"“经典”“殿堂”“情怀”“国宝"五个等级标签,hot字段使用"藏”“热”"冷"三个字标记市场热度,这些信息在列表渲染中会以彩色标签的形式直观呈现。每条数据的num字段为车龄年数(36-69年),在Tab1页面的列表中以"车龄XX年"的形式展示。
专业数据源:老爷车市、修复工坊与配件集市

除主列表外,应用还定义了老爷车市、修复工坊和配件集市三个专业数据源。
// 老爷车市:12 条待售经典车,num 为车龄年
private vinList: GoodsItem[] = [
{ name: '1972 奔驰 280SE 3.5', price: 385000, desc: '1972年 · V8豪华轿跑', score: 90, num: 54, tag: '七十年代', hot: '售' },
{ name: '1966 福特 野马 GT', price: 620000, desc: '1966年 · 美式肌肉代表', score: 92, num: 60, tag: '六十年代', hot: '售' },
{ name: '1983 丰田 AE86', price: 520000, desc: '1983年 · 秋名山传说', score: 93, num: 43, tag: '八十年代', hot: '售' },
{ name: '1977 凯迪拉克 弗利特伍德', price: 298000, desc: '1977年 · 陆地游艇', score: 85, num: 49, tag: '七十年代', hot: '售' },
{ name: '1969 大众 T2 面包车', price: 345000, desc: '1969年 · 嬉皮士之车', score: 89, num: 57, tag: '六十年代', hot: '售' },
{ name: '1985 奔驰 W126 560SEC', price: 210000, desc: '1985年 · 元首级双门', score: 82, num: 41, tag: '八十年代', hot: '售' },
{ name: '1963 沃克斯豪尔 Victor', price: 128000, desc: '1963年 · 英伦家用经典', score: 75, num: 63, tag: '六十年代', hot: '售' },
{ name: '1974 阿尔法·罗密欧 GTV', price: 265000, desc: '1974年 · 意式双门轿跑', score: 88, num: 52, tag: '七十年代', hot: '售' },
{ name: '1990 日产 Skyline R32', price: 680000, desc: '1990年 · 战神GT-R', score: 95, num: 36, tag: '九十年代', hot: '售' },
{ name: '1968 福特 F-100 皮卡', price: 188000, desc: '1968年 · 美式工具车', score: 78, num: 58, tag: '六十年代', hot: '售' },
{ name: '1979 铃木 吉姆尼 SJ10', price: 86000, desc: '1979年 · 轻越野鼻祖', score: 72, num: 47, tag: '七十年代', hot: '售' },
{ name: '1956 雪佛兰 贝莱尔', price: 720000, desc: '1956年 · 美国梦象征', score: 94, num: 70, tag: '五十年代', hot: '售' }
]
// 修复工坊:10 条修复项目,num 为工期天数
private fixList: GoodsItem[] = [
{ name: '1966 福特 野马整车修复', price: 268000, desc: '发动机与车漆全流程', score: 96, num: 180, tag: '精修', hot: '急' },
{ name: '1975 奔驰 W115 底盘整备', price: 68000, desc: '悬挂与制动重建', score: 90, num: 45, tag: '底盘', hot: '缓' },
{ name: '1962 捷豹 XK150 车漆复原', price: 158000, desc: '九道工序手工漆', score: 93, num: 120, tag: '车漆', hot: '急' },
{ name: '1959 凯迪拉克内饰翻新', price: 120000, desc: '真皮与镀铬件重制', score: 88, num: 90, tag: '内饰', hot: '缓' },
{ name: '1970 雪铁龙 DS 液压系统', price: 96000, desc: '液气悬挂专项', score: 91, num: 60, tag: '底盘', hot: '急' },
{ name: '1983 丰田 AE86 车架校正', price: 88000, desc: '事故车回归赛道', score: 87, num: 75, tag: '车架', hot: '缓' },
{ name: '1968 红旗 CA770 内饰复原', price: 320000, desc: '国车级翻新工程', score: 98, num: 300, tag: '精修', hot: '急' },
{ name: '1972 宝马 2002 发动机大修', price: 76000, desc: '化油器重新调校', score: 89, num: 50, tag: '发动机', hot: '缓' },
{ name: '1957 菲亚特 500 电路改造', price: 36000, desc: '全车电路升级', score: 82, num: 40, tag: '电气', hot: '缓' },
{ name: '1979 拉达 2101 钣金修复', price: 28000, desc: '锈蚀面板更换', score: 80, num: 40, tag: '钣金', hot: '急' }
]
// 配件集市:10 条稀有配件,num 为库存件数
private partList: GoodsItem[] = [
{ name: '化油器 Weber 40DCOE', price: 6800, desc: '适配阿尔法与宝马', score: 96, num: 48, tag: '欧系', hot: '珍' },
{ name: '红旗 CA72 原厂大灯总成', price: 15800, desc: '适配红旗CA72', score: 99, num: 60, tag: '国产', hot: '孤品' },
{ name: '捷豹 E-Type 木纹方向盘', price: 9200, desc: '适配E-Type S1', score: 94, num: 45, tag: '英系', hot: '珍' },
{ name: '奔驰 W123 全套镀铬饰条', price: 4200, desc: '适配W123轿车', score: 78, num: 120, tag: '欧系', hot: '常' },
{ name: '甲壳虫 原厂帆布车顶', price: 3600, desc: '适配1200敞篷版', score: 85, num: 90, tag: '欧系', hot: '常' },
{ name: '凯迪拉克 尾翼装饰件', price: 7800, desc: '适配1958年款', score: 92, num: 50, tag: '美系', hot: '珍' },
{ name: '伏尔加 前格栅总成', price: 2900, desc: '适配嘎斯24', score: 74, num: 110, tag: '苏系', hot: '常' },
{ name: '菲亚特 500 里程表芯', price: 880, desc: '适配500全系列', score: 68, num: 200, tag: '意系', hot: '常' },
{ name: '宝马 2002 圆灯支架', price: 1500, desc: '适配2002轿车', score: 72, num: 150, tag: '欧系', hot: '常' },
{ name: '上海 SH760 门把手', price: 1200, desc: '适配SH760A', score: 88, num: 70, tag: '国产', hot: '珍' }
]
老爷车市列表中tag字段按年代分类(五十年代至九十年代),hot字段统一标记为"售"。修复工坊列表中num为工期天数(40-300天),tag为修复项目类别(精修、底盘、车漆、内饰等),hot使用"急"“缓"标记紧迫度。配件集市列表中num为库存件数,score为稀有度指数(68-99),hot使用"珍”“孤品”"常"标记稀有级别。这些差异化的字段语义在各自Tab页面的渲染逻辑中被精确处理,确保了数据的正确呈现。
辅助方法与报价计算引擎

应用定义了一组辅助方法,其中priceText方法用于价格格式化和fixQuote方法用于修复报价动态计算,是本应用最具业务价值的方法设计。
selName(): string {
if (this.selectedItem !== null) {
return this.selectedItem.name
}
return ''
}
selPrice(): number {
if (this.selectedItem !== null) {
return this.selectedItem.price
}
return 0
}
selDesc(): string {
if (this.selectedItem !== null) {
return this.selectedItem.desc
}
return ''
}
selScore(): number {
if (this.selectedItem !== null) {
return this.selectedItem.score
}
return 0
}
selTag(): string {
if (this.selectedItem !== null) {
return this.selectedItem.tag
}
return ''
}
selHot(): string {
if (this.selectedItem !== null) {
return this.selectedItem.hot
}
return ''
}
maxNumOf(list: GoodsItem[]): number {
let m: number = 1
for (let i = 0; i < list.length; i++) {
if (list[i].num > m) {
m = list[i].num
}
}
return m
}
priceText(p: number): string {
let w: number = Math.floor(p / 10000)
let k: number = Math.floor((p % 10000) / 1000)
return w + '.' + k + '万'
}
fixQuote(): number {
let base: number = 68000
if (this.chipA === 1) {
base = 42000
} else if (this.chipA === 2) {
base = 26000
} else if (this.chipA === 3) {
base = 35000
}
let factor: number = 1.0
if (this.chipB === 1) {
factor = 1.6
} else if (this.chipB === 2) {
factor = 2.4
} else if (this.chipB === 3) {
factor = 3.8
}
return Math.floor(base * factor)
}
六个sel*方法遵循统一的防御性编程模式,确保在selectedItem为空时返回安全默认值。maxNumOf方法用于柱状图的归一化计算。priceText方法将数值价格转换为"万"单位的中文价格格式——通过Math.floor(p / 10000)取万位整数,Math.floor((p % 10000) / 1000)取千位整数,拼接为如"138.0万""3.6万"的格式,这种格式化在经典车交易场景中符合中文用户的阅读习惯。
fixQuote方法是修复评估弹窗的核心业务逻辑。它根据chipA(修复项目)确定基础报价(发动机大修68000、内饰翻新42000、车漆修复26000、底盘整备35000),再根据chipB(期望工期)确定乘数因子(3个月1.6倍、6个月2.4倍、1年3.8倍),最终通过Math.floor(base * factor)计算预估报价。这种"基础价+工期系数"的报价模型虽然简化,但清晰地展示了状态驱动业务计算的设计模式——当用户在弹窗中选择不同的修复项目和工期时,报价数字会实时变化并触发UI刷新。
静态头部与底部导航

头部组件集成了品牌标识、搜索入口和消息通知功能,底部导航栏实现七Tab切换。
// 静态头部:标题行 + 搜索胶囊 + 展会信息条
@Builder headerBuilder() {
Column() {
Row() {
Row({ space: 8 }) {
Column() {
Text('经').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(26).height(26).borderRadius(6).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text('复古经典车馆').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.alignItems(VerticalAlign.Center)
Column().layoutWeight(1)
Row({ space: 14 }) {
Text('🔨').fontSize(16)
.onClick(() => { this.chipA = 0; this.chipB = 0; this.showAddModal = true })
Stack() {
Text('✉').fontSize(16)
Text('6').fontSize(8).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.offset({ x: 8, y: -8 })
}.width(24).height(24)
.onClick(() => { this.currentTab = 5 })
}.alignItems(VerticalAlign.Center)
}.width('100%').padding({ left: 16, right: 16, top: 10, bottom: 10 }).alignItems(VerticalAlign.Center)
Row({ space: 8 }) {
Text('🚗').fontSize(14)
Text('搜经典车/配件').fontSize(12).fontColor('#B8A98F').layoutWeight(1)
Text('🔍').fontSize(12)
}.width('100%').height(36).borderRadius(18).backgroundColor('#FAF3E8')
.padding({ left: 14, right: 14 }).alignItems(VerticalAlign.Center)
Row({ space: 8 }) {
Column().width(4).height(12).borderRadius(2).backgroundColor('#A1887F')
Text('秋季经典车展 · 展车征集截止本月底').fontSize(11).fontColor('#FAF3E8').layoutWeight(1)
Text('查看 ›').fontSize(10).fontColor('#A1887F')
}.width('100%').padding({ top: 10, bottom: 4 }).alignItems(VerticalAlign.Center)
}.width('100%').backgroundColor('#8D2F2F')
.padding({ left: 14, right: 14, top: 6, bottom: 10 })
}
头部以复古红#8D2F2F为背景,采用三层垂直布局。顶部标题行左侧为黄铜金#A1887F色方块标识(内含"经"字)和"复古经典车馆"品牌名,右侧为修复锤子按钮(绑定发布藏车弹窗)和消息按钮(通过Stack叠加红色未读角标,绑定跳转到消息Tab)。中间搜索胶囊条使用米白色#FAF3E8背景。底部展会信息条以黄铜金色条标识开头,展示秋季车展征集信息。offset({ x: 8, y: -8 })用于将未读角标偏移到消息图标的右上角位置。
// 底部七个页签
@Builder bottomBuilder() {
Row() {
ForEach(this.tabs, (tab: string, index: number) => {
Column({ space: 2 }) {
Text(tab).fontSize(10).fontColor(this.currentTab === index ? '#8D2F2F' : '#999999')
Column().width(18).height(3).borderRadius(2)
.backgroundColor(this.currentTab === index ? '#8D2F2F' : '#FFFFFF00')
}.onClick(() => { this.currentTab = index })
}, (tab: string) => tab)
}.width('100%').height(56).backgroundColor('#FFFFFF')
}
底部导航栏的选中Tab使用复古红#8D2F2F文字和指示条,未选中Tab使用灰色#999999文字和透明指示条。
经典车展Tab:横幅卡片与列表

经典车展是首页Tab,展示了秋季车展横幅卡片和24台经典车的列表。
// Tab1 经典车展:横幅大卡 + 24 条经典车行式列表
@Builder tab0() {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column({ space: 6 }) {
Text('秋季经典车展 · 第38届').fontSize(17).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('300台传世经典车集结 · 现场拍卖专场').fontSize(11).fontColor('#FAF3E8')
Row({ space: 8 }) {
Text('免票预约').fontSize(10).fontColor('#8D2F2F')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10).backgroundColor('#FAF3E8')
Text('本月截止').fontSize(10).fontColor('#FAF3E8')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10)
.border({ width: 1, color: '#FAF3E8' })
}.alignItems(VerticalAlign.Center)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('🏎').fontSize(38)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').padding(16).borderRadius(14).alignItems(VerticalAlign.Center)
.linearGradient({ angle: 135, colors: [['#8D2F2F', 0], ['#B04A4A', 1]] })
横幅卡片使用了linearGradient线性渐变背景,135度角从复古红#8D2F2F渐变到较浅的红色#B04A4A,营造了车展海报般的视觉氛围。左侧为展会信息文字和两个操作按钮(免票预约以米白底色填充、本月截止以米白描边),右侧为赛车Emoji图标并添加了脉冲缩放动画。linearGradient的colors参数接收一个二维数组,每个子数组的第一个元素为颜色值,第二个元素为渐变位置(0到1),这种声明式的渐变定义方式简洁直观。
ForEach(this.mainList, (item: GoodsItem) => {
Row({ space: 10 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(42).height(42).borderRadius(21).backgroundColor('#8D2F2F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 3 }) {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Column().height(4).borderRadius(2).backgroundColor('#A1887F').width(item.score + '%')
}.width(100).height(4).borderRadius(2).backgroundColor('#F0E6D8')
Text('稀有度 ' + item.score + '% · ' + item.tag + ' · 车龄 ' + item.num + '年')
.fontSize(8).fontColor('#A1887F')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 5 }) {
Text('¥' + this.priceText(item.price)).fontSize(12).fontColor('#E53935').fontWeight(FontWeight.Bold)
Text('热度 ' + item.hot).fontSize(8).fontColor('#999999')
if (item.score > 95) {
Text('榜').fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.alignItems(HorizontalAlign.End)
}.width('100%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
经典车列表每行卡片采用三段式布局。左侧为42x42圆形头像,使用车名首字符item.name.substring(0, 1)作为显示文字。中间信息区包含车名、描述、稀有度进度条(黄铜金填充色、米色背景#F0E6D8)和综合信息文字。右侧价格区使用priceText方法格式化价格,并在稀有度评分超过95分时条件渲染"榜"字标签(带脉冲动画)。if (item.score > 95)的条件渲染是ArkTS声明式UI的重要特性,允许根据数据条件动态控制UI元素的显示与隐藏。
页面交互流程图
以下是用户从浏览经典车到完成修复评估的完整交互流程:
老爷车市与修复工坊Tab
老爷车市Tab展示了车龄分布柱状图和12台待售经典车的双列网格;修复工坊Tab展示了墨绿色统计卡片、工期柱状图和横向滑动的修复案例卡片。
// Tab2 老爷车市:车龄柱状图 + 12 条待售双列网格
@Builder tab1() {
Column({ space: 12 }) {
Column({ space: 10 }) {
Row() {
Text('在售车龄分布').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('HOT').fontSize(9).fontColor('#FFFFFF')
.padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').alignItems(VerticalAlign.Center)
Row() {
ForEach(this.vinList, (item: GoodsItem) => {
Column({ space: 4 }) {
Column().width(16).borderRadius(3).backgroundColor('#8D2F2F')
.height(item.num / this.maxNumOf(this.vinList) * 120)
Text(item.num + '').fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Center)
}, (item: GoodsItem) => item.name)
}.width('100%').alignItems(VerticalAlign.End).justifyContent(FlexAlign.SpaceBetween)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
车龄分布柱状图使用复古红#8D2F2F柱条,高度公式为item.num / this.maxNumOf(this.vinList) * 120,通过justifyContent(FlexAlign.SpaceBetween)实现柱条均匀分布。柱状图标题右侧的"HOT"标签添加了脉冲动画以吸引视觉注意。
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.vinList, (item: GoodsItem) => {
Column({ space: 8 }) {
Row() {
Text(item.tag).fontSize(8).fontColor('#FFFFFF')
.padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(6).backgroundColor('#3E5641')
Column().layoutWeight(1)
Text(item.hot + '·' + item.num + '年').fontSize(8).fontColor('#A1887F')
}.width('100%').alignItems(VerticalAlign.Center)
Column() {
Text(item.name.substring(0, 1)).fontSize(22).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.width('100%').height(64).borderRadius(8).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Text('¥' + this.priceText(item.price)).fontSize(13).fontColor('#E53935').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
if (item.score > 92) {
Text('稀').fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.width('100%').alignItems(VerticalAlign.Center)
}.width('48%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%')
}.width('100%').padding(12)
}
老爷车市双列网格中每张卡片宽度为48%。卡片顶部年代标签使用墨绿#3E5641背景,中间为黄铜金色车辆首字符大图区域(64像素高),下方为车名、描述和价格行。当item.score > 92时条件渲染"稀"字稀有标签(带脉冲动画),帮助用户快速识别高评分稀有车辆。
修复工坊Tab是应用中最具业务深度的页面,包含墨绿色统计卡(展示本月完工38台、在修12项、藏家满意96%三栏数据)、工期柱状图和横向滑动的修复案例卡片(含前后对比"前→后"视觉设计)。
配件集市与车友会Tab
配件集市Tab展示稀有度柱状图和10条配件列表;车友会Tab以社交信息流形式展示藏车故事帖。
// Tab4 配件集市:稀有度柱状图 + 10 条稀有配件列表
@Builder tab3() {
Column({ space: 12 }) {
Column({ space: 10 }) {
Row() {
Text('稀有度指数').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('我要求购').fontSize(11).fontColor('#FFFFFF')
.padding({ left: 12, right: 12, top: 5, bottom: 5 }).borderRadius(12).backgroundColor('#8D2F2F')
.onClick(() => { this.chipA = 0; this.chipB = 0; this.stepNum = 1; this.showWantModal = true })
}.width('100%').alignItems(VerticalAlign.Center)
Row() {
ForEach(this.partList, (item: GoodsItem) => {
Column({ space: 3 }) {
Column().width(16).borderRadius(3).backgroundColor('#A1887F').height(item.score / 100 * 120)
Text(item.num + '').fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Center)
}, (item: GoodsItem) => item.name)
}.width('100%').alignItems(VerticalAlign.End).justifyContent(FlexAlign.SpaceBetween)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
ForEach(this.partList, (item: GoodsItem, index: number) => {
Row({ space: 10 }) {
Column().width(4).height(46).borderRadius(2)
.backgroundColor(index % 3 === 0 ? '#8D2F2F' : (index % 3 === 1 ? '#A1887F' : '#3E5641'))
Column({ space: 3 }) {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Column().height(4).borderRadius(2).backgroundColor('#A1887F').width(item.score + '%')
}.width(110).height(4).borderRadius(2).backgroundColor('#F0E6D8')
Text('稀有度 ' + item.score + '% · 库存 ' + item.num + ' 件 · ' + item.tag + '件')
.fontSize(8).fontColor('#A1887F')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 5 }) {
Text('¥' + Math.floor(item.price)).fontSize(12).fontColor('#E53935').fontWeight(FontWeight.Bold)
if (item.score > 90) {
Text(item.hot).fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
} else {
Text(item.hot).fontSize(8).fontColor('#A1887F')
}
}.alignItems(HorizontalAlign.End)
}.width('100%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
配件集市Tab的稀有度柱状图使用黄铜金#A1887F柱条,高度公式为item.score / 100 * 120——直接除以100而非maxNumOf返回值,因为score字段本身就是0-100的百分比值。配件列表左侧色条使用index % 3循环取色(复古红、黄铜金、墨绿三色交替),为列表增添了视觉色彩节奏。右侧稀有标签使用条件渲染——当item.score > 90时以警示红背景脉冲动画展示,否则以黄铜金色静态展示,实现了稀有级别的视觉分级。
// Tab5 车友会:年代话题 + 10 条藏车故事帖
@Builder tab4() {
Column({ space: 12 }) {
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.topicChips, (t: string, i: number) => {
Text('# ' + t).fontSize(11).fontColor(i % 2 === 0 ? '#8D2F2F' : '#3E5641')
.padding({ left: 10, right: 10, top: 5, bottom: 5 }).borderRadius(14)
.backgroundColor(i % 2 === 0 ? '#F6E4E4' : '#E4EDE6')
.margin({ right: 8, bottom: 6 })
}, (t: string) => t)
}.width('100%')
ForEach(this.clubList, (item: GoodsItem) => {
Column({ space: 8 }) {
Row({ space: 8 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(15).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(36).height(36).borderRadius(18).backgroundColor('#3E5641')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 2 }) {
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold)
Text(item.tag + ' 车主 · 声望 ' + item.score).fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('关注').fontSize(10).fontColor('#8D2F2F')
.padding({ left: 10, right: 10, top: 3, bottom: 3 }).borderRadius(10).backgroundColor('#F6E4E4')
}.alignItems(VerticalAlign.Center)
Text(item.desc).fontSize(11).fontColor('#444444').maxLines(2).lineHeight(16)
Row({ space: 6 }) {
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#A1887F')
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#C9B8A3')
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#8D2F2F')
}.width('100%')
Row() {
Text('阅读 ' + item.num + ' · ' + item.hot + '华帖').fontSize(9).fontColor('#999999')
Column().layoutWeight(1)
Row({ space: 4 }) {
Text('👍').fontSize(12)
Text(item.price + '').fontSize(11).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.alignItems(VerticalAlign.Center)
}.width('100%')
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
车友会Tab的话题标签栏使用i % 2奇偶取色——偶数索引为复古红浅色#F6E4E4,奇数索引为墨绿浅色#E4EDE6,形成了双色交替的话题标签效果。每个藏车故事帖包含墨绿色头像、用户名与声望信息、故事描述(两行省略)、三列图片占位区(黄铜金、浅褐色、复古红三色背景)和底部互动栏。点赞数item.price添加了脉冲动画,阅读量和华帖标记在左侧展示。这种社交信息流设计为藏家社区提供了交流展示的平台。
消息与个人中心Tab
消息Tab以通知列表展示12条消息,通过未读红点脉冲动画提醒用户注意;个人中心Tab展示用户信息和功能菜单。
// Tab6 消息:12 条通知列表,未读红点带脉冲
@Builder tab5() {
Column({ space: 10 }) {
Row() {
Text('消息中心').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('全部已读').fontSize(10).fontColor('#A1887F')
}.width('100%').padding({ left: 2, right: 2 })
ForEach(this.msgList, (item: GoodsItem) => {
Row({ space: 10 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(40).height(40).borderRadius(20)
.backgroundColor(item.hot === '未' ? '#8D2F2F' : '#B8A98F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 3 }) {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Column().layoutWeight(1)
Text(item.tag).fontSize(8).fontColor('#BBBBBB')
}.width('100%').alignItems(VerticalAlign.Center)
Text(item.desc).fontSize(10).fontColor('#999999').maxLines(1)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
if (item.hot === '未') {
Column().width(8).height(8).borderRadius(4).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
消息列表中头像背景色根据item.hot字段动态切换——未读消息使用复古红#8D2F2F背景,已读消息使用浅褐色#B8A98F背景。未读消息右侧条件渲染一个8x8的警示红圆点,添加了脉冲缩放动画效果。这种通过hot字段值(“未"或"已”)驱动视觉差异的设计,简洁有效地实现了消息状态的可视化区分。
// Tab7 我的:墨绿个人卡 + 三格统计 + 六行菜单
@Builder tab6() {
Column({ space: 12 }) {
Row({ space: 12 }) {
Column() {
Text('藏').fontSize(20).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.width(60).height(60).borderRadius(30).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 4 }) {
Text('老陈的车库').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('LV.8 资深经典车收藏家').fontSize(10).fontColor('#CFE0CF')
Text('收藏 38 年 · 主修欧系与国产老车').fontSize(9).fontColor('#CFE0CF')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('›').fontSize(18).fontColor('#CFE0CF')
}.width('100%').padding(16).borderRadius(14).backgroundColor('#3E5641').alignItems(VerticalAlign.Center)
Row() {
Column({ space: 2 }) {
Text('12').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('我的藏车').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(26).backgroundColor('#F0E6D8')
Column({ space: 2 }) {
Text('3').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('修复订单').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(26).backgroundColor('#F0E6D8')
Column({ space: 2 }) {
Text('5').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('展会门票').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
}.width('100%').padding(14).backgroundColor('#FFFFFF').borderRadius(12)
个人中心Tab的顶部为墨绿色#3E5641个人卡,包含黄铜金色头像、用户名"老陈的车库"、等级和收藏信息。下方为三栏统计数据(藏车12台、修复订单3个、展会门票5张),栏间使用1像素宽的米色分隔线。功能菜单通过ForEach渲染menuList数组,每行菜单根据索引i条件渲染不同的Emoji图标和附加信息,点击时根据菜单类型跳转到不同Tab或打开不同弹窗。
弹窗系统:发布藏车与修复评估
应用设计了五类弹窗,以下展示发布藏车弹窗和修复评估弹窗的核心实现。
// 弹框一:发布藏车(底部弹出)
@Builder addModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#8D2F2F')
Text('发布经典车').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showAddModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('出厂年代').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.eraChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#8D2F2F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('当前车况').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.condChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Text('车辆描述').fontSize(12).fontColor('#666666').width('100%')
TextInput({ placeholder: '车况、里程、过户次数等', text: this.inputDesc })
.fontSize(12).height(40).backgroundColor('#F5EFE3').borderRadius(8)
.padding({ left: 10, right: 10 })
.onChange((value: string) => { this.inputDesc = value })
Text('立即发布').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#8D2F2F')
.onClick(() => { this.showAddModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
.onClick((event: ClickEvent) => { event.stopPropagation() })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showAddModal = false })
}
发布藏车弹窗采用底部弹出模式。标题行左侧使用4x16的复古红色条作为标题装饰标识。弹窗包含两组芯片选择器——出厂年代(复古红选中色)和当前车况(黄铜金选中色),两种芯片使用不同选中色帮助用户区分不同维度的选择。TextInput绑定inputDesc状态变量实现双向数据流,onChange回调实时更新状态。constraintSize({ maxHeight: '80%' })限制弹窗最大高度为屏幕的80%。
// 弹框二:修复评估(底部弹出,报价带脉冲)
@Builder fixModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#3E5641')
Text('修复评估').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showFixModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('修复项目').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.fixItemChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#3E5641' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('期望工期').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.fixDurChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Column({ space: 6 }) {
Row() {
Text('预估报价').fontSize(12).fontColor('#666666')
Column().layoutWeight(1)
Text('¥' + this.priceText(this.fixQuote())).fontSize(22).fontColor('#E53935').fontWeight(FontWeight.Bold)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').alignItems(VerticalAlign.Center)
Text('报价随项目与工期浮动,最终以实车勘验为准').fontSize(9).fontColor('#999999').width('100%')
}.width('100%').padding(12).borderRadius(10).backgroundColor('#F5EFE3')
Text('提交评估').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#3E5641')
.onClick(() => { this.showFixModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
.onClick((event: ClickEvent) => { event.stopPropagation() })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showFixModal = false })
}
修复评估弹窗是应用中最具业务深度的弹窗。修复项目选择使用墨绿#3E5641选中色,期望工期选择使用黄铜金#A1887F选中色。预估报价通过this.priceText(this.fixQuote())方法链式调用——先调用fixQuote()根据当前chipA和chipB计算报价数值,再调用priceText()格式化为"万"单位中文价格。报价数字添加了脉冲缩放动画以强调其动态变化的特性,下方还有一行说明文字"报价随项目与工期浮动,最终以实车勘验为准"。这种"状态选择驱动报价计算"的设计模式是声明式UI在业务逻辑层面的典型应用——用户每次切换修复项目或期望工期,chipA或chipB状态变更自动触发fixQuote()重新计算,UI自动刷新报价显示。
配件求购弹窗与详情弹窗
配件求购弹窗包含步进器用于设置求购数量,详情弹窗采用右侧滑出全高面板展示车辆详细信息。
// 弹框三:配件求购(底部弹出,含数量步进器)
@Builder wantModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#A1887F')
Text('配件求购').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showWantModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('配件类型').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.partTypeChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#8D2F2F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('适配年代').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.wantEraChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Row() {
Text('求购数量').fontSize(12).fontColor('#666666')
Column().layoutWeight(1)
Text('-').fontSize(14).fontColor(this.stepNum <= 1 ? '#CCCCCC' : '#8D2F2F')
.width(30).height(30).borderRadius(15).textAlign(TextAlign.Center).backgroundColor('#F5EFE3')
.onClick(() => { if (this.stepNum > 1) { this.stepNum = this.stepNum - 1 } })
Text(this.stepNum + ' 件').fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold)
.width(56).textAlign(TextAlign.Center)
Text('+').fontSize(14).fontColor(this.stepNum >= 10 ? '#CCCCCC' : '#8D2F2F')
.width(30).height(30).borderRadius(15).textAlign(TextAlign.Center).backgroundColor('#F5EFE3')
.onClick(() => { if (this.stepNum < 10) { this.stepNum = this.stepNum + 1 } })
}.width('100%').alignItems(VerticalAlign.Center)
Text('发布求购').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#A1887F')
.onClick(() => { this.showWantModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
.onClick((event: ClickEvent) => { event.stopPropagation() })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showWantModal = false })
}
配件求购弹窗的步进器设计了一个细节:减号和加号按钮的字体颜色根据当前值动态变化——当stepNum <= 1时减号变为灰色#CCCCCC表示不可再减,当stepNum >= 10时加号变为灰色表示不可再加。这种基于状态的条件着色为用户提供了直观的操作边界反馈。步进范围设定为1-10件,覆盖了配件求购的典型数量需求。
// 弹框五:车辆详情(右侧滑出全高面板)
@Builder detailModal() {
Column() {
Column({ space: 12 }) {
Stack() {
Column().width('100%').height(190).borderRadius(12)
.linearGradient({ angle: 135, colors: [['#8D2F2F', 0], ['#5E1F1F', 1]] })
Column({ space: 6 }) {
Text('传世经典').fontSize(11).fontColor('#FAF3E8')
.padding({ left: 10, right: 10, top: 3, bottom: 3 }).borderRadius(10)
.border({ width: 1, color: '#A1887F' })
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text(this.selName().substring(0, 1)).fontSize(52).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.height(190).justifyContent(FlexAlign.Center)
}.width('100%')
Text(this.selName()).fontSize(17).fontColor('#333333').fontWeight(FontWeight.Bold).width('100%')
Text(this.selDesc()).fontSize(10).fontColor('#999999').width('100%')
Row() {
Text('¥' + this.priceText(this.selPrice())).fontSize(24).fontColor('#E53935').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text(this.selTag() + ' · 热度 ' + this.selHot()).fontSize(10).fontColor('#A1887F')
}.width('100%').alignItems(VerticalAlign.Bottom)
Column({ space: 5 }) {
Text('稀有度 ' + this.selScore() + '%').fontSize(10).fontColor('#666666').width('100%')
Row() {
Column().height(6).borderRadius(3).backgroundColor('#A1887F').width(this.selScore() + '%')
}.width('100%').height(6).borderRadius(3).backgroundColor('#F0E6D8')
}.width('100%')
Text('历史档案').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.historyChips, (c: string) => {
Text(c).fontSize(10).fontColor('#3E5641')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10)
.backgroundColor('#E4EDE6')
.margin({ right: 8, bottom: 6 })
}, (c: string) => c)
}.width('100%')
Column().layoutWeight(1)
Text('移出收藏').fontSize(12).fontColor('#999999').width('100%').textAlign(TextAlign.Center)
.padding({ top: 8, bottom: 8 }).borderRadius(18)
.onClick(() => { this.showDeleteModal = true })
Text('预约看车').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#8D2F2F')
.onClick(() => { this.showDetailModal = false })
}.width('80%').height('100%').padding(14).backgroundColor('#FFFFFF')
.onClick((event: ClickEvent) => { event.stopPropagation() })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.End).alignItems(HorizontalAlign.End)
.onClick(() => { this.showDetailModal = false })
}
详情弹窗的面板宽度为80%,通过justifyContent(FlexAlign.End)和alignItems(HorizontalAlign.End)定位到屏幕右侧。顶部为190像素高的Stack容器,底层使用linearGradient实现复古红到深红色#5E1F1F的135度渐变背景,上层叠加"传世经典"标签(带脉冲动画和黄铜金描边)和车名首字符大字。中间展示车名、描述、价格(使用priceText格式化)、稀有度进度条和历史档案标签组(墨绿浅色背景#E4EDE6)。底部为"移出收藏"链接(绑定删除确认弹窗)和"预约看车"按钮(关闭详情弹窗)。
主页面构建
build() {
Stack() {
Column() {
this.headerBuilder()
Scroll() {
Column() {
if (this.currentTab === 0) {
this.tab0()
} else if (this.currentTab === 1) {
this.tab1()
} else if (this.currentTab === 2) {
this.tab2()
} else if (this.currentTab === 3) {
this.tab3()
} else if (this.currentTab === 4) {
this.tab4()
} else if (this.currentTab === 5) {
this.tab5()
} else {
this.tab6()
}
}.width('100%')
}.layoutWeight(1).width('100%')
this.bottomBuilder()
}.width('100%').height('100%')
if (this.showAddModal) {
this.addModal()
}
if (this.showFixModal) {
this.fixModal()
}
if (this.showWantModal) {
this.wantModal()
}
if (this.showDeleteModal) {
this.deleteModal()
}
if (this.showDetailModal) {
this.detailModal()
}
}.width('100%').height('100%')
}
}
build方法以Stack为根容器,内部先放置主内容区(头部+可滚动内容区+底部导航栏),然后通过五个独立的if条件块叠加弹窗组件。Tab切换通过if-else条件链实现,每个条件分支调用对应的@Builder方法渲染Tab页面。Scroll组件包裹Tab内容区实现纵向滚动,layoutWeight(1)使其占据头部和底部导航栏之间的全部剩余空间。这种架构确保了主内容与弹窗之间的独立渲染层级,弹窗通过状态变量控制显隐,不影响主内容区的布局和渲染。
技术点对比
| 技术维度 | 实现方式 | 设计特点 | 适用场景 |
|---|---|---|---|
| 配色体系 | 复古红+黄铜金+米白+墨绿四色 | 怀旧主题色系,模块化色彩分配 | 情怀类垂直领域应用 |
| 渐变背景 | linearGradient(135deg,colors) | 声明式渐变定义,海报级视觉效果 | 横幅卡片与详情头部 |
| 报价引擎 | fixQuote()方法+chipA/chipB状态 | 基础价+工期系数动态计算模型 | 修复评估等参数化报价场景 |
| 价格格式化 | priceText()方法 | 万+千位拼接中文价格格式 | 高客单价商品展示 |
| 条件渲染 | if(item.score>95)动态标签 | 稀有度/库存分级标签动态展示 | 数据分级视觉标识 |
| 柱状图归一化 | score/100*120直接百分比 | 百分比值直接映射柱条高度 | 0-100评分类数据可视化 |
| 三色交替 | index%3循环取色色条 | 复古红/黄铜金/墨绿三色节奏 | 列表项色彩分区 |
| 未读状态 | hot字段值驱动背景色切换 | “未”/"已"字符串条件分支 | 消息列表状态区分 |
| 步进器边界 | stepNum<=1/>=10条件着色 | 灰色禁用态视觉反馈 | 数量步进交互控制 |
| 弹窗层叠 | Stack+五独立if条件块 | 弹窗独立判断、互不干扰 | 多类型弹窗并存管理 |
安装DevEco Studio程序

选择目标安装目录:

设置环境变量,但是需要重启一下:

新建一个空白模板:

设置API为24的模板项目:
初始化项目,自动下载相关依赖:

完整代码:
// 复古经典车馆 · 汽车之家风格经典老爷车收藏场景
// 配色:复古红 8D2F2F / 黄铜金 A1887F / 米白 FAF3E8 / 墨绿 3E5641 / 警示红 E53935
interface GoodsItem {
name: string
price: number
desc: string
score: number
num: number
tag: string
hot: string
}
@Entry
@Component
struct Index {
@State currentTab: number = 0
@State showAddModal: boolean = false
@State showFixModal: boolean = false
@State showWantModal: boolean = false
@State showDeleteModal: boolean = false
@State showDetailModal: boolean = false
@State selectedItem: GoodsItem | null = null
@State chipA: number = 0
@State chipB: number = 0
@State stepNum: number = 1
@State inputDesc: string = ''
private tabs: string[] = ['经典车展', '老爷车市', '修复工坊', '配件集市', '车友会', '消息', '我的']
private eraChips: string[] = ['1950前', '1951-1970', '1971-1990', '1991-2000']
private condChips: string[] = ['原版未修', '修复翻新', '改装升级']
private fixItemChips: string[] = ['发动机大修', '内饰翻新', '车漆修复', '底盘整备']
private fixDurChips: string[] = ['1个月', '3个月', '6个月', '1年']
private partTypeChips: string[] = ['发动机件', '内饰件', '外观件', '电气件']
private wantEraChips: string[] = ['1950前', '1970前', '1990前', '2000前']
private topicChips: string[] = ['五十年代', '六十年代', '七十年代', '肌肉车', '欧陆经典', '东方记忆']
private historyChips: string[] = ['原厂档案', '两次过户', '全程养护', '老照片']
private menuList: string[] = ['我的藏车', '修复订单', '求购配件', '发布藏车', '展会门票', '账号设置']
// 经典车展主列表:24 条传世经典车
private mainList: GoodsItem[] = [
{ name: '1965 大众甲壳虫 1200', price: 128000, desc: '1965年 · 德国原版 · 手动挡', score: 96, num: 61, tag: '稀有', hot: '藏' },
{ name: '1978 奔驰 W123 230E', price: 96000, desc: '1978年 · 德系耐用典范', score: 88, num: 48, tag: '经典', hot: '热' },
{ name: '1972 宝马 2002', price: 185000, desc: '1972年 · 巴伐利亚运动轿车', score: 92, num: 54, tag: '稀有', hot: '热' },
{ name: '1963 捷豹 E-Type S1', price: 1380000, desc: '1963年 · 英伦跑车美学巅峰', score: 98, num: 63, tag: '殿堂', hot: '藏' },
{ name: '1990 保时捷 911 964', price: 890000, desc: '1990年 · 末代风冷手动挡', score: 94, num: 36, tag: '稀有', hot: '热' },
{ name: '1985 伏尔加 嘎斯24', price: 68000, desc: '1985年 · 苏联公务车代表', score: 78, num: 41, tag: '情怀', hot: '冷' },
{ name: '1965 红旗 CA72', price: 2680000, desc: '1965年 · 国礼级检阅车', score: 99, num: 61, tag: '国宝', hot: '藏' },
{ name: '1976 上海 SH760A', price: 155000, desc: '1976年 · 沪上制造记忆', score: 86, num: 50, tag: '情怀', hot: '热' },
{ name: '1969 丰田 2000GT', price: 3600000, desc: '1969年 · 东瀛第一超跑', score: 99, num: 57, tag: '殿堂', hot: '藏' },
{ name: '1961 福特 雷鸟', price: 210000, desc: '1961年 · 美式敞篷豪华', score: 84, num: 65, tag: '经典', hot: '冷' },
{ name: '1970 雪铁龙 DS21', price: 480000, desc: '1970年 · 液压悬挂传奇', score: 93, num: 56, tag: '稀有', hot: '热' },
{ name: '1980 菲亚特 124', price: 72000, desc: '1980年 · 意式家用后驱', score: 74, num: 46, tag: '经典', hot: '冷' },
{ name: '1967 阿尔法·罗密欧 Giulia', price: 520000, desc: '1967年 · 米兰弯道精灵', score: 91, num: 59, tag: '稀有', hot: '热' },
{ name: '1974 沃尔沃 P1800', price: 260000, desc: '1974年 · 北欧长寿纪录', score: 85, num: 52, tag: '经典', hot: '冷' },
{ name: '1958 凯迪拉克 Eldorado', price: 720000, desc: '1958年 · 尾翼时代巅峰', score: 95, num: 68, tag: '稀有', hot: '藏' },
{ name: '1988 宝马 M3 E30', price: 1150000, desc: '1988年 · 直六赛化机器', score: 97, num: 38, tag: '殿堂', hot: '热' },
{ name: '1969 日产 费尔雷迪 Z432', price: 980000, desc: '1969年 · 东瀛S20名机', score: 94, num: 57, tag: '稀有', hot: '藏' },
{ name: '1975 标致 504', price: 58000, desc: '1975年 · 非洲拉力常胜', score: 72, num: 51, tag: '情怀', hot: '冷' },
{ name: '1957 菲亚特 500', price: 165000, desc: '1957年 · 意大利国民小车', score: 87, num: 69, tag: '经典', hot: '热' },
{ name: '1962 奔驰 190SL', price: 880000, desc: '1962年 · 斯图加特敞篷', score: 92, num: 64, tag: '稀有', hot: '藏' },
{ name: '1979 拉达 2101', price: 38000, desc: '1979年 · 战斗民族国民车', score: 66, num: 47, tag: '情怀', hot: '冷' },
{ name: '1984 北京 BJ212', price: 46000, desc: '1984年 · 国产越野活化石', score: 76, num: 42, tag: '情怀', hot: '热' },
{ name: '1968 庞蒂亚克 GTO', price: 960000, desc: '1968年 · 肌肉车鼻祖', score: 95, num: 58, tag: '殿堂', hot: '藏' },
{ name: '1986 丰田 FJ60', price: 235000, desc: '1986年 · 方盒子越野经典', score: 83, num: 40, tag: '经典', hot: '热' }
]
// 老爷车市:12 条待售经典车,num 为车龄年
private vinList: GoodsItem[] = [
{ name: '1972 奔驰 280SE 3.5', price: 385000, desc: '1972年 · V8豪华轿跑', score: 90, num: 54, tag: '七十年代', hot: '售' },
{ name: '1966 福特 野马 GT', price: 620000, desc: '1966年 · 美式肌肉代表', score: 92, num: 60, tag: '六十年代', hot: '售' },
{ name: '1983 丰田 AE86', price: 520000, desc: '1983年 · 秋名山传说', score: 93, num: 43, tag: '八十年代', hot: '售' },
{ name: '1977 凯迪拉克 弗利特伍德', price: 298000, desc: '1977年 · 陆地游艇', score: 85, num: 49, tag: '七十年代', hot: '售' },
{ name: '1969 大众 T2 面包车', price: 345000, desc: '1969年 · 嬉皮士之车', score: 89, num: 57, tag: '六十年代', hot: '售' },
{ name: '1985 奔驰 W126 560SEC', price: 210000, desc: '1985年 · 元首级双门', score: 82, num: 41, tag: '八十年代', hot: '售' },
{ name: '1963 沃克斯豪尔 Victor', price: 128000, desc: '1963年 · 英伦家用经典', score: 75, num: 63, tag: '六十年代', hot: '售' },
{ name: '1974 阿尔法·罗密欧 GTV', price: 265000, desc: '1974年 · 意式双门轿跑', score: 88, num: 52, tag: '七十年代', hot: '售' },
{ name: '1990 日产 Skyline R32', price: 680000, desc: '1990年 · 战神GT-R', score: 95, num: 36, tag: '九十年代', hot: '售' },
{ name: '1968 福特 F-100 皮卡', price: 188000, desc: '1968年 · 美式工具车', score: 78, num: 58, tag: '六十年代', hot: '售' },
{ name: '1979 铃木 吉姆尼 SJ10', price: 86000, desc: '1979年 · 轻越野鼻祖', score: 72, num: 47, tag: '七十年代', hot: '售' },
{ name: '1956 雪佛兰 贝莱尔', price: 720000, desc: '1956年 · 美国梦象征', score: 94, num: 70, tag: '五十年代', hot: '售' }
]
// 修复工坊:10 条修复项目,num 为工期天数
private fixList: GoodsItem[] = [
{ name: '1966 福特 野马整车修复', price: 268000, desc: '发动机与车漆全流程', score: 96, num: 180, tag: '精修', hot: '急' },
{ name: '1975 奔驰 W115 底盘整备', price: 68000, desc: '悬挂与制动重建', score: 90, num: 45, tag: '底盘', hot: '缓' },
{ name: '1962 捷豹 XK150 车漆复原', price: 158000, desc: '九道工序手工漆', score: 93, num: 120, tag: '车漆', hot: '急' },
{ name: '1959 凯迪拉克内饰翻新', price: 120000, desc: '真皮与镀铬件重制', score: 88, num: 90, tag: '内饰', hot: '缓' },
{ name: '1970 雪铁龙 DS 液压系统', price: 96000, desc: '液气悬挂专项', score: 91, num: 60, tag: '底盘', hot: '急' },
{ name: '1983 丰田 AE86 车架校正', price: 88000, desc: '事故车回归赛道', score: 87, num: 75, tag: '车架', hot: '缓' },
{ name: '1968 红旗 CA770 内饰复原', price: 320000, desc: '国车级翻新工程', score: 98, num: 300, tag: '精修', hot: '急' },
{ name: '1972 宝马 2002 发动机大修', price: 76000, desc: '化油器重新调校', score: 89, num: 50, tag: '发动机', hot: '缓' },
{ name: '1957 菲亚特 500 电路改造', price: 36000, desc: '全车电路升级', score: 82, num: 40, tag: '电气', hot: '缓' },
{ name: '1979 拉达 2101 钣金修复', price: 28000, desc: '锈蚀面板更换', score: 80, num: 40, tag: '钣金', hot: '急' }
]
// 配件集市:10 条稀有配件,num 为库存件数
private partList: GoodsItem[] = [
{ name: '化油器 Weber 40DCOE', price: 6800, desc: '适配阿尔法与宝马', score: 96, num: 48, tag: '欧系', hot: '珍' },
{ name: '红旗 CA72 原厂大灯总成', price: 15800, desc: '适配红旗CA72', score: 99, num: 60, tag: '国产', hot: '孤品' },
{ name: '捷豹 E-Type 木纹方向盘', price: 9200, desc: '适配E-Type S1', score: 94, num: 45, tag: '英系', hot: '珍' },
{ name: '奔驰 W123 全套镀铬饰条', price: 4200, desc: '适配W123轿车', score: 78, num: 120, tag: '欧系', hot: '常' },
{ name: '甲壳虫 原厂帆布车顶', price: 3600, desc: '适配1200敞篷版', score: 85, num: 90, tag: '欧系', hot: '常' },
{ name: '凯迪拉克 尾翼装饰件', price: 7800, desc: '适配1958年款', score: 92, num: 50, tag: '美系', hot: '珍' },
{ name: '伏尔加 前格栅总成', price: 2900, desc: '适配嘎斯24', score: 74, num: 110, tag: '苏系', hot: '常' },
{ name: '菲亚特 500 里程表芯', price: 880, desc: '适配500全系列', score: 68, num: 200, tag: '意系', hot: '常' },
{ name: '宝马 2002 圆灯支架', price: 1500, desc: '适配2002轿车', score: 72, num: 150, tag: '欧系', hot: '常' },
{ name: '上海 SH760 门把手', price: 1200, desc: '适配SH760A', score: 88, num: 70, tag: '国产', hot: '珍' }
]
// 车友会:10 条藏车故事帖,price 为点赞数,num 为阅读量
private clubList: GoodsItem[] = [
{ name: '老周藏车', price: 328, desc: '我的甲壳虫开了三十年,从校门一直开到女儿婚礼现场,方向盘磨出了包浆。', score: 96, num: 486, tag: '甲壳虫', hot: '精' },
{ name: '锡兰骑士', price: 214, desc: '捷豹E-Type修复日记第十二篇:等了半年的木纹方向盘终于到货,装上的瞬间眼眶湿了。', score: 92, num: 452, tag: 'E-Type', hot: '热' },
{ name: '北岭老爷子', price: 176, desc: '红旗CA72的每一颗螺丝,都是那个年代的自豪,拆装全程我做了三本笔记。', score: 98, num: 500, tag: '红旗CA72', hot: '精' },
{ name: 'V24车库', price: 198, desc: '伏尔加在东北零下三十度一次点火成功,化油器预热改装立了大功。', score: 80, num: 361, tag: '嘎斯24', hot: '热' },
{ name: '胶片公路', price: 245, desc: '开着W123走了一趟川藏线,老车比想象中可靠,全程只补了一次机油。', score: 88, num: 428, tag: 'W123', hot: '热' },
{ name: '沪上司机', price: 132, desc: '上海SH760的方向盘上还留着父亲的掌温,这台车我们家守了三代。', score: 90, num: 297, tag: 'SH760', hot: '温' },
{ name: '弯道信徒', price: 289, desc: '阿尔法Giulia的化油器声浪,值得凌晨四点起床去山路跑一圈。', score: 93, num: 465, tag: 'Giulia', hot: '精' },
{ name: '尾翼时代', price: 167, desc: '凯迪拉克Eldorado的尾翼,是火箭时代留给公路的浪漫,没有之一。', score: 95, num: 388, tag: 'Eldorado', hot: '热' },
{ name: '风冷教徒', price: 203, desc: '964跑了一趟草原,风冷机的味道混着草香,那就是自由的味道。', score: 91, num: 410, tag: '911 964', hot: '热' },
{ name: '雪铁龙大叔', price: 158, desc: 'DS21的液压悬挂,让快七十岁的它依然会在路口优雅地跳舞。', score: 89, num: 345, tag: 'DS21', hot: '温' }
]
// 消息:12 条通知,tag 为时间,hot 标记未读
private msgList: GoodsItem[] = [
{ name: '拍卖出价助手', price: 128, desc: '您关注的甲壳虫1200出现新竞价', score: 96, num: 420, tag: '刚刚', hot: '未' },
{ name: '修复工坊王师傅', price: 96, desc: '您的车漆复原进度已过半', score: 90, num: 388, tag: '09:12', hot: '未' },
{ name: '配件集市', price: 64, desc: '您求购的化油器已到货一件', score: 84, num: 350, tag: '昨天', hot: '未' },
{ name: '车友会通知', price: 210, desc: '周末老城巡游活动开始报名', score: 88, num: 468, tag: '昨天', hot: '未' },
{ name: '藏家老周', price: 56, desc: '那台伏尔加我朋友有兴趣,聊聊?', score: 80, num: 300, tag: '周四', hot: '已' },
{ name: '展会组委会', price: 88, desc: '您的展车资料审核已通过', score: 92, num: 410, tag: '周四', hot: '未' },
{ name: '修复工坊李工', price: 45, desc: '底盘整备方案已发送至车库', score: 82, num: 280, tag: '周三', hot: '已' },
{ name: '配件集市', price: 72, desc: '红旗CA72大灯总成补货提醒', score: 90, num: 366, tag: '08-20', hot: '未' },
{ name: '车友弯道信徒', price: 39, desc: '周末山路小聚,带上你的Giulia', score: 78, num: 260, tag: '08-19', hot: '已' },
{ name: '拍卖保证金通知', price: 120, desc: '保证金已退还至原支付账户', score: 86, num: 395, tag: '08-18', hot: '已' },
{ name: '车友尾翼时代', price: 51, desc: '分享一张Eldorado的绝美照片', score: 76, num: 240, tag: '08-17', hot: '已' },
{ name: '系统安全提醒', price: 66, desc: '账号于新设备登录,请确认', score: 94, num: 500, tag: '08-16', hot: '未' }
]
// 以下为取值辅助函数
selName(): string {
if (this.selectedItem !== null) {
return this.selectedItem.name
}
return ''
}
selPrice(): number {
if (this.selectedItem !== null) {
return this.selectedItem.price
}
return 0
}
selDesc(): string {
if (this.selectedItem !== null) {
return this.selectedItem.desc
}
return ''
}
selScore(): number {
if (this.selectedItem !== null) {
return this.selectedItem.score
}
return 0
}
selTag(): string {
if (this.selectedItem !== null) {
return this.selectedItem.tag
}
return ''
}
selHot(): string {
if (this.selectedItem !== null) {
return this.selectedItem.hot
}
return ''
}
maxNumOf(list: GoodsItem[]): number {
let m: number = 1
for (let i = 0; i < list.length; i++) {
if (list[i].num > m) {
m = list[i].num
}
}
return m
}
priceText(p: number): string {
let w: number = Math.floor(p / 10000)
let k: number = Math.floor((p % 10000) / 1000)
return w + '.' + k + '万'
}
fixQuote(): number {
let base: number = 68000
if (this.chipA === 1) {
base = 42000
} else if (this.chipA === 2) {
base = 26000
} else if (this.chipA === 3) {
base = 35000
}
let factor: number = 1.0
if (this.chipB === 1) {
factor = 1.6
} else if (this.chipB === 2) {
factor = 2.4
} else if (this.chipB === 3) {
factor = 3.8
}
return Math.floor(base * factor)
}
// 静态头部:标题行 + 搜索胶囊 + 展会信息条
@Builder headerBuilder() {
Column() {
Row() {
Row({ space: 8 }) {
Column() {
Text('经').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(26).height(26).borderRadius(6).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text('复古经典车馆').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.alignItems(VerticalAlign.Center)
Column().layoutWeight(1)
Row({ space: 14 }) {
Text('🔨').fontSize(16)
.onClick(() => { this.chipA = 0; this.chipB = 0; this.showAddModal = true })
Stack() {
Text('✉').fontSize(16)
Text('6').fontSize(8).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.offset({ x: 8, y: -8 })
}.width(24).height(24)
.onClick(() => { this.currentTab = 5 })
}.alignItems(VerticalAlign.Center)
}.width('100%').padding({ left: 16, right: 16, top: 10, bottom: 10 }).alignItems(VerticalAlign.Center)
Row({ space: 8 }) {
Text('🚗').fontSize(14)
Text('搜经典车/配件').fontSize(12).fontColor('#B8A98F').layoutWeight(1)
Text('🔍').fontSize(12)
}.width('100%').height(36).borderRadius(18).backgroundColor('#FAF3E8')
.padding({ left: 14, right: 14 }).alignItems(VerticalAlign.Center)
Row({ space: 8 }) {
Column().width(4).height(12).borderRadius(2).backgroundColor('#A1887F')
Text('秋季经典车展 · 展车征集截止本月底').fontSize(11).fontColor('#FAF3E8').layoutWeight(1)
Text('查看 ›').fontSize(10).fontColor('#A1887F')
}.width('100%').padding({ top: 10, bottom: 4 }).alignItems(VerticalAlign.Center)
}.width('100%').backgroundColor('#8D2F2F')
.padding({ left: 14, right: 14, top: 6, bottom: 10 })
}
// 底部七个页签
@Builder bottomBuilder() {
Row() {
ForEach(this.tabs, (tab: string, index: number) => {
Column({ space: 2 }) {
Text(tab).fontSize(10).fontColor(this.currentTab === index ? '#8D2F2F' : '#999999')
Column().width(18).height(3).borderRadius(2)
.backgroundColor(this.currentTab === index ? '#8D2F2F' : '#FFFFFF00')
}.onClick(() => { this.currentTab = index })
}, (tab: string) => tab)
}.width('100%').height(56).backgroundColor('#FFFFFF')
}
// Tab1 经典车展:横幅大卡 + 24 条经典车行式列表
@Builder tab0() {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column({ space: 6 }) {
Text('秋季经典车展 · 第38届').fontSize(17).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('300台传世经典车集结 · 现场拍卖专场').fontSize(11).fontColor('#FAF3E8')
Row({ space: 8 }) {
Text('免票预约').fontSize(10).fontColor('#8D2F2F')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10).backgroundColor('#FAF3E8')
Text('本月截止').fontSize(10).fontColor('#FAF3E8')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10)
.border({ width: 1, color: '#FAF3E8' })
}.alignItems(VerticalAlign.Center)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('🏎').fontSize(38)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').padding(16).borderRadius(14).alignItems(VerticalAlign.Center)
.linearGradient({ angle: 135, colors: [['#8D2F2F', 0], ['#B04A4A', 1]] })
ForEach(this.mainList, (item: GoodsItem) => {
Row({ space: 10 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(42).height(42).borderRadius(21).backgroundColor('#8D2F2F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 3 }) {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Column().height(4).borderRadius(2).backgroundColor('#A1887F').width(item.score + '%')
}.width(100).height(4).borderRadius(2).backgroundColor('#F0E6D8')
Text('稀有度 ' + item.score + '% · ' + item.tag + ' · 车龄 ' + item.num + '年')
.fontSize(8).fontColor('#A1887F')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 5 }) {
Text('¥' + this.priceText(item.price)).fontSize(12).fontColor('#E53935').fontWeight(FontWeight.Bold)
Text('热度 ' + item.hot).fontSize(8).fontColor('#999999')
if (item.score > 95) {
Text('榜').fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.alignItems(HorizontalAlign.End)
}.width('100%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
// Tab2 老爷车市:车龄柱状图 + 12 条待售双列网格
@Builder tab1() {
Column({ space: 12 }) {
Column({ space: 10 }) {
Row() {
Text('在售车龄分布').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('HOT').fontSize(9).fontColor('#FFFFFF')
.padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').alignItems(VerticalAlign.Center)
Row() {
ForEach(this.vinList, (item: GoodsItem) => {
Column({ space: 4 }) {
Column().width(16).borderRadius(3).backgroundColor('#8D2F2F')
.height(item.num / this.maxNumOf(this.vinList) * 120)
Text(item.num + '').fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Center)
}, (item: GoodsItem) => item.name)
}.width('100%').justifyContent(FlexAlign.SpaceBetween)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.vinList, (item: GoodsItem) => {
Column({ space: 8 }) {
Row() {
Text(item.tag).fontSize(8).fontColor('#FFFFFF')
.padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(6).backgroundColor('#3E5641')
Column().layoutWeight(1)
Text(item.hot + '·' + item.num + '年').fontSize(8).fontColor('#A1887F')
}.width('100%').alignItems(VerticalAlign.Center)
Column() {
Text(item.name.substring(0, 1)).fontSize(22).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.width('100%').height(64).borderRadius(8).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Text('¥' + this.priceText(item.price)).fontSize(13).fontColor('#E53935').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
if (item.score > 92) {
Text('稀').fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.width('100%').alignItems(VerticalAlign.Center)
}.width('48%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%')
}.width('100%').padding(12)
}
// Tab3 修复工坊:墨绿统计卡 + 工期柱状图 + 横向案例卡
@Builder tab2() {
Column({ space: 12 }) {
Column({ space: 10 }) {
Row({ space: 6 }) {
Text('🔧').fontSize(18)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text('修复工坊').fontSize(15).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text(this.item0State()).fontSize(9).fontColor('#CFE0CF')
}.width('100%').alignItems(VerticalAlign.Center)
Row() {
Column({ space: 2 }) {
Text('38').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('本月完工').fontSize(9).fontColor('#CFE0CF')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(28).backgroundColor('#5A7460')
Column({ space: 2 }) {
Text('12').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('在修项目').fontSize(9).fontColor('#CFE0CF')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(28).backgroundColor('#5A7460')
Column({ space: 2 }) {
Text('96%').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('藏家满意').fontSize(9).fontColor('#CFE0CF')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
}.width('100%').padding({ top: 6, bottom: 6 })
Text('立即评估').fontSize(12).fontColor('#3E5641').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 8, bottom: 8 }).borderRadius(16).backgroundColor('#FAF3E8')
.onClick(() => { this.chipA = 0; this.chipB = 0; this.showFixModal = true })
}.width('100%').padding(14).borderRadius(14).backgroundColor('#3E5641')
Column({ space: 10 }) {
Text('修复工期一览(天)').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Row() {
ForEach(this.fixList, (item: GoodsItem) => {
Column({ space: 3 }) {
Column().width(14).borderRadius(3).backgroundColor('#3E5641')
.height(item.num / this.maxNumOf(this.fixList) * 100)
Text(item.num + '').fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Center)
}, (item: GoodsItem) => item.name)
}.width('100%').justifyContent(FlexAlign.SpaceBetween)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
Column({ space: 10 }) {
Text('修复案例').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Scroll() {
Row({ space: 10 }) {
ForEach(this.fixList, (item: GoodsItem) => {
Column({ space: 6 }) {
Row({ space: 5 }) {
Column() {
Text('前').fontSize(10).fontColor('#FFFFFF')
}.width(52).height(38).borderRadius(6).backgroundColor('#7A7A7A')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text('→').fontSize(10).fontColor('#A1887F')
Column() {
Text('后').fontSize(10).fontColor('#FFFFFF')
}.width(52).height(38).borderRadius(6).backgroundColor('#8D2F2F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
}.alignItems(VerticalAlign.Center)
Text(item.name).fontSize(11).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text('工期 ' + item.num + '天 · ' + item.tag + ' · ' + item.hot).fontSize(8).fontColor('#999999')
if (item.score > 92) {
Text('口碑精选').fontSize(8).fontColor('#FFFFFF')
.padding({ left: 6, right: 6, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.width(150).padding(10).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}
}.scrollable(ScrollDirection.Horizontal).width('100%')
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
}.width('100%').padding(12)
}
// Tab4 配件集市:稀有度柱状图 + 10 条稀有配件列表
@Builder tab3() {
Column({ space: 12 }) {
Column({ space: 10 }) {
Row() {
Text('稀有度指数').fontSize(14).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('我要求购').fontSize(11).fontColor('#FFFFFF')
.padding({ left: 12, right: 12, top: 5, bottom: 5 }).borderRadius(12).backgroundColor('#8D2F2F')
.onClick(() => { this.chipA = 0; this.chipB = 0; this.stepNum = 1; this.showWantModal = true })
}.width('100%').alignItems(VerticalAlign.Center)
Row() {
ForEach(this.partList, (item: GoodsItem) => {
Column({ space: 3 }) {
Column().width(16).borderRadius(3).backgroundColor('#A1887F').height(item.score / 100 * 120)
Text(item.num + '').fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Center)
}, (item: GoodsItem) => item.name)
}.width('100%').justifyContent(FlexAlign.SpaceBetween)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
ForEach(this.partList, (item: GoodsItem, index: number) => {
Row({ space: 10 }) {
Column().width(4).height(46).borderRadius(2)
.backgroundColor(index % 3 === 0 ? '#8D2F2F' : (index % 3 === 1 ? '#A1887F' : '#3E5641'))
Column({ space: 3 }) {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(item.desc).fontSize(9).fontColor('#999999').maxLines(1)
Row() {
Column().height(4).borderRadius(2).backgroundColor('#A1887F').width(item.score + '%')
}.width(110).height(4).borderRadius(2).backgroundColor('#F0E6D8')
Text('稀有度 ' + item.score + '% · 库存 ' + item.num + ' 件 · ' + item.tag + '件')
.fontSize(8).fontColor('#A1887F')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 5 }) {
Text('¥' + Math.floor(item.price)).fontSize(12).fontColor('#E53935').fontWeight(FontWeight.Bold)
if (item.score > 90) {
Text(item.hot).fontSize(8).fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 1, bottom: 1 }).borderRadius(8).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
} else {
Text(item.hot).fontSize(8).fontColor('#A1887F')
}
}.alignItems(HorizontalAlign.End)
}.width('100%').padding(10).backgroundColor('#FFFFFF').borderRadius(12)
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
// Tab5 车友会:年代话题 + 10 条藏车故事帖
@Builder tab4() {
Column({ space: 12 }) {
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.topicChips, (t: string, i: number) => {
Text('# ' + t).fontSize(11).fontColor(i % 2 === 0 ? '#8D2F2F' : '#3E5641')
.padding({ left: 10, right: 10, top: 5, bottom: 5 }).borderRadius(14)
.backgroundColor(i % 2 === 0 ? '#F6E4E4' : '#E4EDE6')
.margin({ right: 8, bottom: 6 })
}, (t: string) => t)
}.width('100%')
ForEach(this.clubList, (item: GoodsItem) => {
Column({ space: 8 }) {
Row({ space: 8 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(15).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(36).height(36).borderRadius(18).backgroundColor('#3E5641')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 2 }) {
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold)
Text(item.tag + ' 车主 · 声望 ' + item.score).fontSize(8).fontColor('#999999')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('关注').fontSize(10).fontColor('#8D2F2F')
.padding({ left: 10, right: 10, top: 3, bottom: 3 }).borderRadius(10).backgroundColor('#F6E4E4')
}.alignItems(VerticalAlign.Center)
Text(item.desc).fontSize(11).fontColor('#444444').maxLines(2).lineHeight(16)
Row({ space: 6 }) {
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#A1887F')
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#C9B8A3')
Column().layoutWeight(1).height(64).borderRadius(8).backgroundColor('#8D2F2F')
}.width('100%')
Row() {
Text('阅读 ' + item.num + ' · ' + item.hot + '华帖').fontSize(9).fontColor('#999999')
Column().layoutWeight(1)
Row({ space: 4 }) {
Text('👍').fontSize(12)
Text(item.price + '').fontSize(11).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.alignItems(VerticalAlign.Center)
}.width('100%')
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
// Tab6 消息:12 条通知列表,未读红点带脉冲
@Builder tab5() {
Column({ space: 10 }) {
Row() {
Text('消息中心').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('全部已读').fontSize(10).fontColor('#A1887F')
}.width('100%').padding({ left: 2, right: 2 })
ForEach(this.msgList, (item: GoodsItem) => {
Row({ space: 10 }) {
Column() {
Text(item.name.substring(0, 1)).fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}.width(40).height(40).borderRadius(20)
.backgroundColor(item.hot === '未' ? '#8D2F2F' : '#B8A98F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 3 }) {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Column().layoutWeight(1)
Text(item.tag).fontSize(8).fontColor('#BBBBBB')
}.width('100%').alignItems(VerticalAlign.Center)
Text(item.desc).fontSize(10).fontColor('#999999').maxLines(1)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
if (item.hot === '未') {
Column().width(8).height(8).borderRadius(4).backgroundColor('#E53935')
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
.onClick(() => { this.selectedItem = item; this.showDetailModal = true })
}, (item: GoodsItem) => item.name)
}.width('100%').padding(12)
}
// Tab7 我的:墨绿个人卡 + 三格统计 + 六行菜单
@Builder tab6() {
Column({ space: 12 }) {
Row({ space: 12 }) {
Column() {
Text('藏').fontSize(20).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.width(60).height(60).borderRadius(30).backgroundColor('#A1887F')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Column({ space: 4 }) {
Text('老陈的车库').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('LV.8 资深经典车收藏家').fontSize(10).fontColor('#CFE0CF')
Text('收藏 38 年 · 主修欧系与国产老车').fontSize(9).fontColor('#CFE0CF')
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('›').fontSize(18).fontColor('#CFE0CF')
}.width('100%').padding(16).borderRadius(14).backgroundColor('#3E5641').alignItems(VerticalAlign.Center)
Row() {
Column({ space: 2 }) {
Text('12').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('我的藏车').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(26).backgroundColor('#F0E6D8')
Column({ space: 2 }) {
Text('3').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('修复订单').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
Column().width(1).height(26).backgroundColor('#F0E6D8')
Column({ space: 2 }) {
Text('5').fontSize(16).fontColor('#8D2F2F').fontWeight(FontWeight.Bold)
Text('展会门票').fontSize(9).fontColor('#999999')
}.alignItems(HorizontalAlign.Center).layoutWeight(1)
}.width('100%').padding(14).backgroundColor('#FFFFFF').borderRadius(12)
Column({ space: 8 }) {
ForEach(this.menuList, (m: string, i: number) => {
Row({ space: 10 }) {
Text(i === 0 ? '🚗' : (i === 1 ? '🔧' : (i === 2 ? '🧩' : (i === 3 ? '📤' : (i === 4 ? '🎟' : '⚙'))))).fontSize(14)
Text(m).fontSize(13).fontColor('#333333').layoutWeight(1)
Text(i === 0 ? '12 台' : (i === 1 ? '进行中 3' : (i === 4 ? '未使用 5' : ''))).fontSize(10).fontColor('#999999')
Text('›').fontSize(14).fontColor('#CCCCCC')
}.width('100%').padding({ left: 14, right: 14, top: 13, bottom: 13 })
.backgroundColor('#FFFFFF').borderRadius(12).alignItems(VerticalAlign.Center)
.onClick(() => {
if (i === 0) {
this.currentTab = 0
} else if (i === 1) {
this.currentTab = 2
} else if (i === 2) {
this.chipA = 0; this.chipB = 0; this.stepNum = 1; this.showWantModal = true
} else if (i === 3) {
this.chipA = 0; this.chipB = 0; this.showAddModal = true
} else if (i === 4) {
this.currentTab = 0
} else {
this.currentTab = 6
}
})
}, (m: string) => m)
}.width('100%')
}.width('100%').padding(12)
}
// 弹框一:发布藏车(底部弹出)
@Builder addModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#8D2F2F')
Text('发布经典车').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showAddModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('出厂年代').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.eraChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#8D2F2F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('当前车况').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.condChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Text('车辆描述').fontSize(12).fontColor('#666666').width('100%')
TextInput({ placeholder: '车况、里程、过户次数等', text: this.inputDesc })
.fontSize(12).height(40).backgroundColor('#F5EFE3').borderRadius(8)
.padding({ left: 10, right: 10 })
.onChange((value: string) => { this.inputDesc = value })
Text('立即发布').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#8D2F2F')
.onClick(() => { this.showAddModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showAddModal = false })
}
// 弹框二:修复评估(底部弹出,报价带脉冲)
@Builder fixModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#3E5641')
Text('修复评估').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showFixModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('修复项目').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.fixItemChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#3E5641' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('期望工期').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.fixDurChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Column({ space: 6 }) {
Row() {
Text('预估报价').fontSize(12).fontColor('#666666')
Column().layoutWeight(1)
Text('¥' + this.priceText(this.fixQuote())).fontSize(22).fontColor('#E53935').fontWeight(FontWeight.Bold)
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}.width('100%').alignItems(VerticalAlign.Center)
Text('报价随项目与工期浮动,最终以实车勘验为准').fontSize(9).fontColor('#999999').width('100%')
}.width('100%').padding(12).borderRadius(10).backgroundColor('#F5EFE3')
Text('提交评估').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#3E5641')
.onClick(() => { this.showFixModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showFixModal = false })
}
// 弹框三:配件求购(底部弹出,含数量步进器)
@Builder wantModal() {
Column() {
Column().layoutWeight(1)
Column({ space: 14 }) {
Row() {
Column().width(4).height(16).borderRadius(2).backgroundColor('#A1887F')
Text('配件求购').fontSize(16).fontColor('#333333').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('×').fontSize(18).fontColor('#999999').padding(4)
.onClick(() => { this.showWantModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
Text('配件类型').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.partTypeChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipA === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipA === i ? '#8D2F2F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipA = i })
}, (c: string) => c)
}.width('100%')
Text('适配年代').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.wantEraChips, (c: string, i: number) => {
Text(c).fontSize(12).fontColor(this.chipB === i ? '#FFFFFF' : '#666666')
.padding({ left: 14, right: 14, top: 6, bottom: 6 }).borderRadius(14)
.backgroundColor(this.chipB === i ? '#A1887F' : '#F5EFE3')
.margin({ right: 8, bottom: 6 })
.onClick(() => { this.chipB = i })
}, (c: string) => c)
}.width('100%')
Row() {
Text('求购数量').fontSize(12).fontColor('#666666')
Column().layoutWeight(1)
Text('-').fontSize(14).fontColor(this.stepNum <= 1 ? '#CCCCCC' : '#8D2F2F')
.width(30).height(30).borderRadius(15).textAlign(TextAlign.Center).backgroundColor('#F5EFE3')
.onClick(() => { if (this.stepNum > 1) { this.stepNum = this.stepNum - 1 } })
Text(this.stepNum + ' 件').fontSize(13).fontColor('#333333').fontWeight(FontWeight.Bold)
.width(56).textAlign(TextAlign.Center)
Text('+').fontSize(14).fontColor(this.stepNum >= 10 ? '#CCCCCC' : '#8D2F2F')
.width(30).height(30).borderRadius(15).textAlign(TextAlign.Center).backgroundColor('#F5EFE3')
.onClick(() => { if (this.stepNum < 10) { this.stepNum = this.stepNum + 1 } })
}.width('100%').alignItems(VerticalAlign.Center)
Text('发布求购').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#A1887F')
.onClick(() => { this.showWantModal = false })
}.width('100%').padding(16).backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.constraintSize({ maxHeight: '80%' })
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showWantModal = false })
}
// 弹框四:删除确认(居中警示小卡)
@Builder deleteModal() {
Column() {
Column({ space: 14 }) {
Column() {
Text('⚠').fontSize(28).fontColor('#FFFFFF')
}.width(56).height(56).borderRadius(28).backgroundColor('#E53935')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
Text('确认删除该藏车收藏?').fontSize(15).fontColor('#333333').fontWeight(FontWeight.Bold)
Text('删除后相关展车预约将一并取消').fontSize(10).fontColor('#999999')
Row({ space: 12 }) {
Text('取消').fontSize(14).fontColor('#666666').textAlign(TextAlign.Center).layoutWeight(1)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#F5EFE3')
.onClick(() => { this.showDeleteModal = false })
Text('删除').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center).layoutWeight(1)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#E53935')
.onClick(() => { this.showDeleteModal = false; this.showDetailModal = false })
}.width('100%').alignItems(VerticalAlign.Center)
}.width('72%').padding(20).borderRadius(12).backgroundColor('#FFFFFF')
.alignItems(HorizontalAlign.Center)
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
.onClick(() => { this.showDeleteModal = false })
}
// 弹框五:车辆详情(右侧滑出全高面板)
@Builder detailModal() {
Column() {
Column({ space: 12 }) {
Stack() {
Column().width('100%').height(190).borderRadius(12)
.linearGradient({ angle: 135, colors: [['#8D2F2F', 0], ['#5E1F1F', 1]] })
Column({ space: 6 }) {
Text('传世经典').fontSize(11).fontColor('#FAF3E8')
.padding({ left: 10, right: 10, top: 3, bottom: 3 }).borderRadius(10)
.border({ width: 1, color: '#A1887F' })
.scale({ x: 1.05, y: 1.05 }).animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text(this.selName().substring(0, 1)).fontSize(52).fontColor('#FAF3E8').fontWeight(FontWeight.Bold)
}.height(190).justifyContent(FlexAlign.Center)
}.width('100%')
Text(this.selName()).fontSize(17).fontColor('#333333').fontWeight(FontWeight.Bold).width('100%')
Text(this.selDesc()).fontSize(10).fontColor('#999999').width('100%')
Row() {
Text('¥' + this.priceText(this.selPrice())).fontSize(24).fontColor('#E53935').fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text(this.selTag() + ' · 热度 ' + this.selHot()).fontSize(10).fontColor('#A1887F')
}.width('100%').alignItems(VerticalAlign.Bottom)
Column({ space: 5 }) {
Text('稀有度 ' + this.selScore() + '%').fontSize(10).fontColor('#666666').width('100%')
Row() {
Column().height(6).borderRadius(3).backgroundColor('#A1887F').width(this.selScore() + '%')
}.width('100%').height(6).borderRadius(3).backgroundColor('#F0E6D8')
}.width('100%')
Text('历史档案').fontSize(12).fontColor('#666666').width('100%')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.historyChips, (c: string) => {
Text(c).fontSize(10).fontColor('#3E5641')
.padding({ left: 10, right: 10, top: 4, bottom: 4 }).borderRadius(10)
.backgroundColor('#E4EDE6')
.margin({ right: 8, bottom: 6 })
}, (c: string) => c)
}.width('100%')
Column().layoutWeight(1)
Text('移出收藏').fontSize(12).fontColor('#999999').width('100%').textAlign(TextAlign.Center)
.padding({ top: 8, bottom: 8 }).borderRadius(18)
.onClick(() => { this.showDeleteModal = true })
Text('预约看车').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 }).borderRadius(20).backgroundColor('#8D2F2F')
.onClick(() => { this.showDetailModal = false })
}.width('80%').height('100%').padding(14).backgroundColor('#FFFFFF')
}.width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.End).alignItems(HorizontalAlign.End)
.onClick(() => { this.showDetailModal = false })
}
item0State(): string {
return '本季度接单 ' + this.fixList.length + ' 项'
}
build() {
Stack() {
Column() {
this.headerBuilder()
Scroll() {
Column() {
if (this.currentTab === 0) {
this.tab0()
} else if (this.currentTab === 1) {
this.tab1()
} else if (this.currentTab === 2) {
this.tab2()
} else if (this.currentTab === 3) {
this.tab3()
} else if (this.currentTab === 4) {
this.tab4()
} else if (this.currentTab === 5) {
this.tab5()
} else {
this.tab6()
}
}.width('100%')
}.layoutWeight(1).width('100%')
this.bottomBuilder()
}.width('100%').height('100%')
if (this.showAddModal) {
this.addModal()
}
if (this.showFixModal) {
this.fixModal()
}
if (this.showWantModal) {
this.wantModal()
}
if (this.showDeleteModal) {
this.deleteModal()
}
if (this.showDetailModal) {
this.detailModal()
}
}.width('100%').height('100%')
}
}
总结

本文深入解析了一款基于HarmonyOS ArkTS声明式UI框架构建的复古经典车馆应用页面的完整实现。该应用以复古红、黄铜金、米白和墨绿四色体系构建了浓郁的怀旧视觉基调,通过七大Tab页面(经典车展、老爷车市、修复工坊、配件集市、车友会、消息、我的)和五类弹窗(发布藏车、修复评估、配件求购、删除确认、车辆详情)实现了从经典车浏览到修复评估再到配件求购的完整业务链路。
在数据可视化方面,应用实现了三种柱状图——车龄分布图(使用maxNumOf归一化)、修复工期一览图和稀有度指数图(使用score/100*120直接百分比映射),通过linearGradient线性渐变实现了横幅卡片和详情弹窗头部的海报级视觉效果。在业务逻辑方面,fixQuote()方法通过"基础价+工期系数"的二元参数模型实现了修复报价的动态计算,priceText()方法将数值价格格式化为中文"万"单位,这两个方法的设计体现了状态驱动业务计算的核心思想。在交互细节方面,步进器通过stepNum <= 1和stepNum >= 10的条件着色提供了操作边界反馈,消息列表通过hot字段值"未"/"已"驱动头像背景色和未读红点的条件渲染,配件列表通过index % 3循环取色实现了三色交替的视觉节奏。
从工程架构角度,该应用的设计体现了HarmonyOS ArkTS开发的多个关键实践:统一GoodsItem接口复用覆盖78条六类业务数据、@Builder方法封装实现七大Tab和五类弹窗的UI复用、Stack条件渲染实现弹窗层叠管理、stopPropagation精确控制事件冒泡、constraintSize({ maxHeight: '80%' })限制弹窗高度。这些技术实践为汽车后市场情怀类垂直应用和需要多维度数据展示、参数化报价计算、多类型弹窗交互的复杂业务场景提供了可借鉴的声明式UI实现方案。
更多推荐




所有评论(0)