HarmonyOS 6实现@Builder装饰器构建独立的弹窗组件,bindContentCover方法绑定全屏覆盖弹窗
引言

近年来,文化创意产业在中国市场迎来了前所未有的发展机遇。从故宫文创的爆款出圈到敦煌联名的跨界合作,从国潮文具的崛起到手账文化的流行,文创周边已成为年轻消费群体表达文化认同与审美追求的重要载体。在这一市场趋势下,开发一款整合国潮文创、联名IP、文具用品、手账模板与社区交流的一站式电商应用,具有显著的商业价值与文化意义。多多文创周边应用正是基于这一业务愿景构建的HarmonyOS原生应用,旨在为文创爱好者提供从商品发现到购买收藏的全链路服务体验。
在技术架构层面,本应用基于HarmonyOS ArkTS声明式UI开发框架构建,采用组件化架构设计。主入口组件DuoDuoCulturalApp负责全局状态管理与Tab路由调度,六个业务Tab组件分别承载国潮、联名、文具、手账、社区与个人中心的功能模块。应用通过@State装饰器实现响应式数据流,@Builder装饰器构建独立的弹窗组件,bindContentCover方法绑定全屏覆盖弹窗。interface类型定义确保了数据的类型安全,常量数组初始化提供了完整的离线数据支撑。整个应用的代码结构清晰,组件间通过回调函数实现通信,状态变更驱动UI自动更新,体现了ArkTS声明式编程的核心优势。
在业务设计层面,多多文创周边应用覆盖了文创电商的六大核心场景。国潮Tab以故宫、敦煌、青花瓷等传统文化元素为分类维度,展示精选文创大卡与双列商品列表。联名Tab以IP联名系列为核心,提供联名IP横滑展示、联名时间轴与联名商品列表三个层次的信息呈现。文具Tab整合了八大文具分类网格、限时特价横条与热门文具列表。手账Tab展示手账模板横滑卡片、热门标签云与手账社区动态。社区Tab构建了文创爱好者的话题互动与内容分享生态。个人中心集成了会员等级展示、消费统计柱状图、订单管理、地址管理与收藏列表等完整的用户功能。应用视觉采用文创墨青(#006064)与鎏金(#FFB300)的双色调主题,底色为清雅水墨调(#F0F7FA),营造出兼具传统文化底蕴与现代设计感的产品氛围。
一、类型定义与数据模型设计

本应用通过ArkTS的interface机制定义了覆盖文创全业务域的类型体系,包括文创商品、国潮标签、联名IP、联名商品、文具分类、文具商品、手账模板、手账社区动态、社区帖子、话题、订单、收藏、地址、柱状图数据与规格选项等十五种数据类型。
// 文创商品类型定义
interface CulturalItem78 {
id: number
name: string
price: number
originalPrice: number
origin: string
material: string
rating: number
sales: number
tags: string[]
color: string
desc: string
}
// 国潮分类标签类型
interface GuochaoTag78 {
label: string
color: string
count: number
}
// 联名IP类型定义
interface CollabIP78 {
id: number
name: string
brand: string
date: string
items: number
status: string
color: string
desc: string
}
// 联名商品类型定义
interface CollabItem78 {
id: number
name: string
ip: string
price: number
originalPrice: number
stock: number
limited: boolean
color: string
rating: number
tags: string[]
}
// 文具分类类型定义
interface StationeryCat78 {
label: string
icon: string
color: string
bg: string
count: number
}
// 文具商品类型定义
interface StationeryItem78 {
id: number
name: string
brand: string
price: number
originalPrice: number
rating: number
sales: number
tags: string[]
color: string
flashSale: boolean
}
// 手账模板类型定义
interface JournalTemplate78 {
id: number
name: string
style: string
price: number
pages: number
color: string
popular: boolean
}
// 手账社区动态类型定义
interface JournalPost78 {
id: number
author: string
template: string
content: string
likes: number
comments: number
images: number
timeAgo: string
tags: string[]
avatarColor: string
}
// 订单类型定义
interface OrderItem78 {
id: number
product: string
date: string
amount: number
status: string
tracking: string
color: string
}
// 地址类型定义
interface AddressItem78 {
id: number
name: string
phone: string
address: string
isDefault: boolean
tag: string
}
// 规格选项类型定义
interface SpecOption78 {
id: number
name: string
price: number
color: string
}
类型定义体系的设计体现了对文创电商业务场景的深入理解。CulturalItem78包含了origin(产地)和material(材质)两个文创特有字段,这些信息对文创消费者具有重要的决策参考价值。CollabItem78的limited布尔字段和stock库存字段支持限量商品与售罄状态的精确表达。StationeryItem78的flashSale布尔字段驱动了限时特价标识的显示逻辑。JournalTemplate78的popular字段和pages字段分别控制热度标识与页数信息展示。AddressItem78的isDefault字段和tag字段实现了默认地址标记与地址分类(公司/家)的功能。SpecOption78通过price字段支持规格加价计算,为购买弹窗的动态定价提供了数据基础。
二、静态数据初始化与业务内容填充

应用通过常量数组定义了丰富的静态业务数据,覆盖国潮分类标签、文创商品列表、联名IP系列、联名商品、文具分类、文具商品、手账模板、手账社区动态、社区帖子、热门话题、订单、收藏、地址与消费统计等全部业务场景。
// 国潮分类标签数据
const GUOCHAO_TAGS_78: GuochaoTag78[] = [
{ label: '故宫文创', color: '#8D6E63', count: 128 },
{ label: '敦煌飞天', color: '#FFB300', count: 96 },
{ label: '青花瓷', color: '#1565C0', count: 85 },
{ label: '十二生肖', color: '#C2185B', count: 72 },
{ label: '山海经', color: '#006064', count: 64 },
{ label: '二十四节气', color: '#2E7D32', count: 58 }
]
// 文创商品数据
const CULTURAL_ITEMS_78: CulturalItem78[] = [
{ id: 1, name: '故宫千里江山书签套装', price: 38, originalPrice: 58, origin: '故宫博物院', material: '黄铜+丝绸', rating: 4.9, sales: 8542, tags: ['爆款', '限量'], color: '#006064', desc: '千里江山图复刻书签,丝绸流苏' },
{ id: 2, name: '敦煌飞天手账本', price: 48, originalPrice: 68, origin: '敦煌博物馆', material: '特种纸', rating: 4.8, sales: 6235, tags: ['热销'], color: '#FFB300', desc: '莫高窟壁画封面,内页4种格式' },
{ id: 3, name: '青花瓷纹胶带套装', price: 28, originalPrice: 42, origin: '景德镇', material: '和纸胶带', rating: 4.7, sales: 4856, tags: ['手账必备'], color: '#1565C0', desc: '6卷青花瓷纹样,宽窄各3卷' },
{ id: 4, name: '十二生肖金属徽章', price: 12, originalPrice: 18, origin: '国博文创', material: '珐琅', rating: 4.9, sales: 12653, tags: ['盲盒', '收藏'], color: '#C2185B', desc: '可选生肖,金属珐琅工艺' },
{ id: 5, name: '山海经异兽插画明信片', price: 22, originalPrice: 35, origin: '原创设计', material: '特种纸', rating: 4.8, sales: 3567, tags: ['原创'], color: '#006064', desc: '12张异兽插画,烫金工艺' },
{ id: 6, name: '二十四节气印章套装', price: 68, originalPrice: 98, origin: '非遗文创', material: '石质', rating: 4.9, sales: 2876, tags: ['非遗', '礼盒'], color: '#2E7D32', desc: '24枚节气印章+印泥套装' },
{ id: 7, name: '千里江山丝巾', price: 128, originalPrice: 188, origin: '故宫文创', material: '桑蚕丝', rating: 5.0, sales: 1542, tags: ['高端', '礼赠'], color: '#006064', desc: '90×90cm真丝,千里江山图案' },
{ id: 8, name: '诗词折扇(竹骨)', price: 45, originalPrice: 68, origin: '苏州', material: '竹+宣纸', rating: 4.7, sales: 3654, tags: ['夏日'], color: '#8D6E63', desc: '手绘水墨诗词,14骨竹扇' }
]
// 联名IP数据
const COLLAB_IPS_78: CollabIP78[] = [
{ id: 1, name: '故宫×三星堆', brand: '故宫文创', date: '2026-08', items: 28, status: '热销中', color: '#8D6E63', desc: '青铜面具×故宫纹样联名系列' },
{ id: 2, name: '大英博物馆×国博', brand: '国家博物馆', date: '2026-07', items: 15, status: '热销中', color: '#006064', desc: '东西方文明碰撞联名系列' },
{ id: 3, name: '敦煌×星巴克', brand: '敦煌博物馆', date: '2026-09', items: 12, status: '即将发售', color: '#FFB300', desc: '飞天纹样限量杯具联名' },
{ id: 4, name: '国博×泡泡玛特', brand: '泡泡玛特', date: '2026-06', items: 8, status: '售罄', color: '#C2185B', desc: '文物盲盒系列,已售罄' },
{ id: 5, name: '三星堆×MOLLY', brand: '三星堆博物馆', date: '2026-09', items: 10, status: '预售中', color: '#1565C0', desc: '青铜面具MOLLY联名手办' },
{ id: 6, name: '故宫×乐高', brand: '乐高', date: '2026-10', items: 5, status: '即将发售', color: '#00897B', desc: '太和殿积木联名套装' }
]
// 联名商品数据
const COLLAB_ITEMS_78: CollabItem78[] = [
{ id: 1, name: '三星堆青铜面具徽章', ip: '故宫×三星堆', price: 25, originalPrice: 38, stock: 356, limited: true, color: '#8D6E63', rating: 4.9, tags: ['限量', '联名'] },
{ id: 2, name: '故宫三星堆联名手账', ip: '故宫×三星堆', price: 58, originalPrice: 88, stock: 128, limited: true, color: '#006064', rating: 4.8, tags: ['限量', '手账'] },
{ id: 3, name: '大英博物馆胶带套装', ip: '大英博物馆×国博', price: 35, originalPrice: 52, stock: 678, limited: false, color: '#006064', rating: 4.7, tags: ['联名'] },
{ id: 4, name: '敦煌飞天限量马克杯', ip: '敦煌×星巴克', price: 88, originalPrice: 128, stock: 0, limited: true, color: '#FFB300', rating: 5.0, tags: ['预售', '限量'] },
{ id: 5, name: '国博文物盲盒', ip: '国博×泡泡玛特', price: 69, originalPrice: 99, stock: 0, limited: true, color: '#C2185B', rating: 4.9, tags: ['盲盒', '售罄'] }
]
// 文具分类数据
const STATIONERY_CATS_78: StationeryCat78[] = [
{ label: '钢笔', icon: '✒️', color: '#006064', bg: '#E0F7FA', count: 128 },
{ label: '墨水', icon: '💧', color: '#1565C0', bg: '#E3F2FD', count: 96 },
{ label: '笔记本', icon: '📓', color: '#FFB300', bg: '#FFF8E1', count: 85 },
{ label: '胶带', icon: '🎨', color: '#C2185B', bg: '#FCE4EC', count: 72 },
{ label: '印章', icon: '🔖', color: '#2E7D32', bg: '#E8F5E9', count: 64 },
{ label: '贴纸', icon: '🌸', color: '#6A1B9A', bg: '#F3E5F5', count: 58 },
{ label: '尺规', icon: '📐', color: '#00897B', bg: '#E0F2F1', count: 45 },
{ label: '收纳', icon: '📦', color: '#E65100', bg: '#FFF3E0', count: 38 }
]
// 手账模板数据
const JOURNAL_TEMPLATES_78: JournalTemplate78[] = [
{ id: 1, name: '水墨丹青', style: '中国风', price: 58, pages: 160, color: '#006064', popular: true },
{ id: 2, name: '敦煌飞天', style: '壁画风', price: 68, pages: 200, color: '#FFB300', popular: true },
{ id: 3, name: '青花瓷韵', style: '青花风', price: 48, pages: 120, color: '#1565C0', popular: false },
{ id: 4, name: '山海经异', style: '神兽风', price: 78, pages: 180, color: '#2E7D32', popular: true },
{ id: 5, name: '二十四节气', style: '节气风', price: 88, pages: 240, color: '#8D6E63', popular: false },
{ id: 6, name: '十二生肖', style: '生肖风', price: 52, pages: 144, color: '#C2185B', popular: true }
]
// 消费柱状图数据
const BAR_DATA_78: BarData78[] = [
{ label: '3月', value: 380, color: '#4DB6AC' },
{ label: '4月', value: 520, color: '#4DB6AC' },
{ label: '5月', value: 450, color: '#4DB6AC' },
{ label: '6月', value: 680, color: '#4DB6AC' },
{ label: '7月', value: 820, color: '#4DB6AC' },
{ label: '8月', value: 1180, color: '#006064' }
]
// 规格选项数据
const SPEC_OPTIONS_78: SpecOption78[] = [
{ id: 1, name: '标准版', price: 0, color: '#006064' },
{ id: 2, name: '礼盒版', price: 20, color: '#FFB300' },
{ id: 3, name: '限定版', price: 50, color: '#C2185B' }
]
// 会员等级数据
const MEMBER_LEVELS_78: string[] = ['青铜会员', '白银会员', '黄金会员', '铂金会员', '钻石会员']
静态数据的设计体现了文创行业的业务特点。CULTURAL_ITEMS_78中每件商品都标注了origin(产地)和material(材质),如"故宫博物院"产地的"黄铜+丝绸"材质书签,这些信息对文创消费者具有重要的文化认同价值。COLLAB_IPS_78的status字段涵盖了"热销中"、“即将发售”、“售罄”、"预售中"四种状态,完整描述了联名IP的生命周期。COLLAB_ITEMS_78的stock字段为0时触发售罄状态的UI切换,limited布尔字段驱动限量标签的显示。SPEC_OPTIONS_78通过price字段支持标准版、礼盒版与限定版的差异化定价,为购买弹窗的动态价格计算提供了数据基础。
三、主入口组件与Tab路由架构

主入口组件DuoDuoCulturalApp是整个应用的状态中枢,管理着Tab切换、各类弹窗显示状态、商品选择状态与表单输入状态等全局数据。
@Entry
@Component
struct DuoDuoCulturalApp {
@State currentTab: number = 0
@State showBuyDialog: boolean = false
@State showCancelDialog: boolean = false
@State showEditAddressDialog: boolean = false
@State showDeleteFavDialog: boolean = false
@State selectedItemId: number = 0
@State selectedSpecId: number = 1
@State buyQuantity: number = 1
@State deliveryMethod: number = 0
@State buyNotes: string = ''
@State editAddressName: string = ''
@State editAddressPhone: string = ''
@State editAddressDetail: string = ''
@State cancelTargetId: number = 0
private tabs: string[] = ['国潮', '联名', '文具', '手账', '社区', '我的']
private deliveryOptions: string[] = ['普通配送 ¥5', '顺丰速运 ¥12', '同城急送 ¥8']
build() {
Column() {
// 顶部头部区域
Column() {
Row() {
Column() {
Text('多多文创')
.fontSize(22)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Text('国潮·联名·手账精选')
.fontSize(11)
.fontColor('rgba(255,255,255,0.8)')
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Row() {
Text('🛒').fontSize(18).fontColor('#FFFFFF')
}
.width(36).height(36).justifyContent(FlexAlign.Center)
.backgroundColor('rgba(255,255,255,0.2)')
.borderRadius(18)
}
.width('100%').height(56).padding({ left: 16, right: 16 })
.alignItems(VerticalAlign.Center)
Row() {
Text('搜"故宫文创""敦煌联名"...')
.fontSize(13)
.fontColor('rgba(255,255,255,0.6)')
.layoutWeight(1)
Text('🔍').fontSize(16).fontColor('rgba(255,255,255,0.6)')
}
.width('100%').height(36).margin({ top: 4 })
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(255,255,255,0.15)')
.borderRadius(20)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.linearGradient({ angle: 135, colors: [['#006064', 0], ['#004D40', 1]] })
.padding({ top: 8, bottom: 12, left: 16, right: 16 })
// Tab内容区
Stack() {
Column() {
if (this.currentTab === 0) {
GuochaoTab78({
onBuy: (id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
}
})
}
if (this.currentTab === 1) {
CollabTab78({
onBuy: ((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 2) {
StationeryTab78({
onBuy:((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 3) {
JournalTab78({
onBuy:((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 4) {
CommunityTab78()
}
if (this.currentTab === 5) {
ProfileTab78({
onEditAddress: () => {
this.showEditAddressDialog = true
},
onCancelOrder: (id: number) => {
this.cancelTargetId = id
this.showCancelDialog = true
},
onDeleteFav: (id: number) => {
this.cancelTargetId = id
this.showDeleteFavDialog = true
}
})
}
}
.width('100%').height('100%')
}
// 底部Tab栏
Row() {
ForEach(this.tabs, (tab: string, idx: number) => {
Column() {
Text(this.getTabIcon(idx))
.fontSize(20)
.fontColor(this.currentTab === idx ? '#006064' : '#999999')
Text(tab)
.fontSize(10)
.fontColor(this.currentTab === idx ? '#006064' : '#999999')
.margin({ top: 2 })
}
.layoutWeight(1)
.height(56)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentTab = idx
})
}, (tab: string) => tab)
}
.width('100%')
.height(56)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
}
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
.bindContentCover(this.showCancelDialog, this.buildCancelCover())
.bindContentCover(this.showDeleteFavDialog, this.buildDeleteFavCover())
}
private getTabIcon(idx: number): string {
const icons: string[] = ['🏮', '🤝', '✒️', '📓', '💬', '👤']
return idx < icons.length ? icons[idx] : '📋'
}
}
主入口组件的头部区域采用从#006064到#004D40的135度线性渐变,营造出墨青色的品牌视觉基调。头部布局将品牌名称"多多文创"与副标题"国潮·联名·手账精选"纵向排列在左侧,购物车图标按钮置于右侧。搜索栏的placeholder文字"搜’故宫文创’‘敦煌联名’…"通过示例关键词引导用户的搜索行为,这种设计在电商应用中能有效提升搜索转化率。
Tab路由机制通过条件渲染if语句实现,currentTab的状态值决定了当前激活的Tab组件。与前一个应用不同的是,该应用的头部布局采用了Column嵌套Column的结构,品牌信息以垂直排列方式展示,这种设计在头部高度有限的情况下实现了品牌名称与宣传语的双行展示。底部Tab栏的激活色为#006064墨青色,与应用主题色保持一致。bindContentCover绑定了取消订单弹窗与删除收藏弹窗,确保这两个确认操作的全屏覆盖效果。
四、购买弹窗与规格选择交互

购买弹窗是电商应用的核心交互组件,本应用的购买弹窗集成了规格选择、配送方式、数量调节、备注填写与价格汇总等完整的购买流程。
@Builder
buildBuySheet() {
Column() {
Row() {
Text('购买文创').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('✕').fontSize(20).fontColor('#999999').onClick(() => { this.showBuyDialog = false })
}
.width('100%').padding(16)
Scroll() {
Column() {
// 规格选择
Text('选择规格').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
Row() {
ForEach(SPEC_OPTIONS_78, (spec: SpecOption78) => {
Column() {
Text(spec.name).fontSize(12).fontColor(this.selectedSpecId === spec.id ? '#FFFFFF' : '#666666')
if (spec.price > 0) {
Text('+' + spec.price + '元').fontSize(9).fontColor(this.selectedSpecId === spec.id ? 'rgba(255,255,255,0.8)' : '#AAAAAA').margin({ top: 2 })
} else {
Text('无加价').fontSize(9).fontColor(this.selectedSpecId === spec.id ? 'rgba(255,255,255,0.8)' : '#AAAAAA').margin({ top: 2 })
}
}
.layoutWeight(1).padding(10)
.borderRadius(10)
.backgroundColor(this.selectedSpecId === spec.id ? spec.color : '#F5F5F5')
.margin({ right: 8 })
.onClick(() => { this.selectedSpecId = spec.id })
}, (spec: SpecOption78) => spec.id.toString())
}
.width('100%').margin({ top: 8 })
// 配送方式
Text('配送方式').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ top: 16 })
Column() {
ForEach(this.deliveryOptions, (opt: string, idx: number) => {
Row() {
Radio({ value: idx.toString(), group: 'delivery' })
.checked(this.deliveryMethod === idx)
.onChange((checked: boolean) => { if (checked) { this.deliveryMethod = idx } })
Text(opt).fontSize(12).fontColor('#333333').margin({ left: 8 })
Text('').layoutWeight(1)
Text(this.deliveryMethod === idx ? '✓' : '').fontSize(14).fontColor('#006064')
}
.width('100%').padding(10)
.margin({ top: 4 })
.borderRadius(8)
.backgroundColor(this.deliveryMethod === idx ? '#E0F7FA' : '#F5F5F5')
.onClick(() => { this.deliveryMethod = idx })
}, (opt: string, idx: number) => idx.toString())
}
.width('100%')
// 数量
Row() {
Text('购买数量').fontSize(14).fontColor('#333333')
Text('').layoutWeight(1)
Row() {
Button() {
Text('-').fontSize(16).fontColor('#666666')
}
.width(32).height(32)
.backgroundColor('#F5F5F5')
.borderRadius(16)
.onClick(() => { if (this.buyQuantity > 1) { this.buyQuantity-- } })
Text(this.buyQuantity.toString()).fontSize(14).fontColor('#333333').width(40).textAlign(TextAlign.Center)
Button() {
Text('+').fontSize(16).fontColor('#666666')
}
.width(32).height(32)
.backgroundColor('#F5F5F5')
.borderRadius(16)
.onClick(() => { this.buyQuantity++ })
}
}
.width('100%').margin({ top: 16 })
// 备注
Text('备注').fontSize(14).fontColor('#333333').margin({ top: 16 })
TextArea({ text: this.buyNotes, placeholder: '如有定制需求请备注...' })
.width('100%').height(60)
.margin({ top: 8 })
.borderRadius(10)
.backgroundColor('#F5F5F5')
.onChange((val: string) => { this.buyNotes = val })
// 价格汇总
Row() {
Text('合计').fontSize(13).fontColor('#666666')
Text('').layoutWeight(1)
Text('¥' + this.getTotalPrice()).fontSize(20).fontColor('#FFB300').fontWeight(FontWeight.Bold)
}
.width('100%').margin({ top: 20, bottom: 12 })
}
.width('100%').padding({ left: 16, right: 16, bottom: 16 })
}
.constraintSize({ maxHeight: '55%' })
Row() {
Button() {
Text('确认下单').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
}
.layoutWeight(1).height(48)
.backgroundColor('#006064')
.borderRadius(24)
.onClick(() => { this.showBuyDialog = false })
}
.width('100%').padding(16)
}
.width('100%')
}
private getTotalPrice(): number {
let spec: SpecOption78 | undefined = SPEC_OPTIONS_78.find((s: SpecOption78) => s.id === this.selectedSpecId)
let specPrice: number = spec ? spec.price : 0
let deliveryPrice: number = 5
if (this.deliveryMethod === 1) { deliveryPrice = 12 }
if (this.deliveryMethod === 2) { deliveryPrice = 8 }
return (38 + specPrice) * this.buyQuantity + deliveryPrice
}
购买弹窗的设计体现了电商场景下的精细化交互设计。规格选择区域通过ForEach遍历SPEC_OPTIONS_78渲染三个规格选项,每个选项在选中时背景色变为规格自身的颜色,未选中时为浅灰色。规格的加价信息通过条件表达式动态展示——price大于0时显示"+X元",否则显示"无加价"。配送方式区域使用Radio单选组件配合行级高亮,选中行背景色变为浅青色#E0F7FA并显示勾选标记,这种双重视觉反馈确保了用户对当前选择的清晰认知。
数量调节器采用减号按钮、数字显示与加号按钮的三段式布局,减号按钮在buyQuantity为1时阻止进一步减少,防止了无效操作。getTotalPrice方法实现了完整的动态定价逻辑:基础价格38元加上规格加价,乘以购买数量,再加上配送费用。配送费用根据deliveryMethod的值动态计算——普通配送5元、顺丰速运12元、同城急送8元。这种价格计算方式确保了用户在修改任何购买参数时都能看到实时更新的总价。
五、国潮Tab与文创商品展示

国潮Tab是应用的首页内容,通过国潮分类标签云、精选文创大卡与双列商品列表三大板块,为用户提供了丰富的文创商品发现体验。
@Component
struct GuochaoTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 国潮分类标签
Text('国潮分类').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(GUOCHAO_TAGS_78, (tag: GuochaoTag78) => {
Row() {
Text(tag.label).fontSize(12).fontColor(tag.color)
Text(tag.count + '件').fontSize(9).fontColor('#AAAAAA').margin({ left: 4 })
}
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.backgroundColor(tag.color + '15')
.borderRadius(16)
.border({ width: 1, color: tag.color + '30' })
}, (tag: GuochaoTag78) => tag.label)
}
.width('100%').padding({ left: 16, right: 16 })
// 精选大卡
Text('精选文创').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
Row() {
Column() {
Text('故宫千里江山系列').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('黄铜书签·丝巾·马克杯').fontSize(11).fontColor('rgba(255,255,255,0.8)').margin({ top: 4 })
Row() {
Text('¥38').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('起').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ left: 2 })
Text('').layoutWeight(1)
Text('限时分发').fontSize(10).fontColor('#FFFFFF').backgroundColor('rgba(255,255,255,0.25)').padding({ left: 8, right: 8, top: 2, bottom: 2 }).borderRadius(10)
}
.width('100%').margin({ top: 12 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('🏮').fontSize(48)
}
.width('100%').padding(16)
}
.width('100%').margin({ left: 16, right: 16, top: 8 })
.linearGradient({ angle: 135, colors: [['#006064', 0], ['#004D40', 1]] })
.borderRadius(16)
// 双列文创商品
Text('热门文创').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Grid() {
ForEach(CULTURAL_ITEMS_78, (item: CulturalItem78) => {
GridItem() {
Column() {
Column() {
Text('🎨').fontSize(32)
}
.width('100%').height(80)
.backgroundColor(item.color + '20')
.borderRadius({ topLeft: 12, topRight: 12 })
.justifyContent(FlexAlign.Center)
Column() {
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(2)
Text(item.origin + ' · ' + item.material).fontSize(9).fontColor('#AAAAAA').margin({ top: 4 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4, top: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
Row() {
Text('★').fontSize(10).fontColor('#FFB300')
Text(item.rating.toString()).fontSize(10).fontColor('#FFB300').margin({ left: 2 })
Text(item.sales + '人付款').fontSize(9).fontColor('#AAAAAA').margin({ left: 8 })
}
.margin({ top: 4 })
Row() {
Text('¥').fontSize(12).fontColor('#FFB300')
Text(item.price.toString()).fontSize(16).fontColor('#FFB300').fontWeight(FontWeight.Bold)
Text('¥' + item.originalPrice).fontSize(10).fontColor('#CCCCCC').decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
Text('').layoutWeight(1)
}
.margin({ top: 4 })
Button() {
Text('购买').fontSize(12).fontColor('#FFFFFF')
}
.width('100%').height(28).margin({ top: 8 })
.backgroundColor(item.color)
.borderRadius(14)
.onClick(() => { this.onBuy(item.id) })
}
.padding(8)
}
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}
}, (item: CulturalItem78) => item.id.toString())
}
.columnsTemplate('1fr 1fr')
.rowsGap(12).columnsGap(12)
.padding(16)
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
国潮分类标签区域使用Flex容器配合FlexWrap.Wrap实现自动换行布局,六个标签(故宫文创、敦煌飞天、青花瓷、十二生肖、山海经、二十四节气)各自携带独立颜色配置。每个标签的背景色使用标签颜色加’15’透明度后缀,边框色使用标签颜色加’30’透明度后缀,这种颜色拼接技术实现了与主色调一致的半透明视觉效果。标签内部以Row布局水平排列标签名称与商品数量,圆角16的胶囊外形使标签具有亲和力。
精选文创大卡采用横向布局,左侧展示系列名称、商品描述、起步价格与限时分发标签,右侧放置大型灯笼emoji作为视觉装饰。整个卡片使用与头部一致的墨青色渐变背景,文字采用白色系配色确保可读性。双列商品列表通过Grid的columnsTemplate(‘1fr 1fr’)实现两列等宽布局,每个商品卡片的图片占位区使用商品颜色加’20’透明度作为背景,标签区域使用商品颜色加’15’透明度,形成了同一商品内多层次的色彩表达。
六、联名Tab与时间轴布局

联名Tab是本应用最具特色的模块之一,它通过联名IP横滑展示、联名时间轴与联名商品列表三个层次,构建了完整的联名产品信息架构。
@Component
struct CollabTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 联名IP横滑
Text('联名IP系列').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Scroll() {
Row() {
ForEach(COLLAB_IPS_78, (ip: CollabIP78) => {
Column() {
Column() {
Text('🤝').fontSize(28)
}
.width(140).height(80)
.backgroundColor(ip.color)
.borderRadius({ topLeft: 12, topRight: 12 })
.justifyContent(FlexAlign.Center)
Column() {
Text(ip.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(ip.brand).fontSize(9).fontColor('#AAAAAA').margin({ top: 2 })
Row() {
Text(ip.status).fontSize(9).fontColor(ip.color).backgroundColor(ip.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
Text(ip.items + '款').fontSize(9).fontColor('#AAAAAA').margin({ left: 4 })
}
.margin({ top: 4 })
Text(ip.date).fontSize(9).fontColor('#AAAAAA').margin({ top: 2 })
}
.padding(8)
}
.width(140).margin({ right: 12 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}, (ip: CollabIP78) => ip.id.toString())
}
.padding({ left: 16, right: 16 })
}
.scrollable(ScrollDirection.Horizontal)
.width('100%').margin({ top: 8 })
// 联名时间轴
Text('联名时间轴').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
ForEach(COLLAB_IPS_78, (ip: CollabIP78, idx: number) => {
Row() {
Column() {
Circle({ width: 12, height: 12 }).fill(ip.color)
if (idx < COLLAB_IPS_78.length - 1) {
Line()
.width(2).layoutWeight(1)
.backgroundColor('#E0E0E0')
.margin({ top: 4 })
}
}
.height('100%')
Column() {
Row() {
Text(ip.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium)
Text(ip.date).fontSize(10).fontColor('#AAAAAA').margin({ left: 8 })
Text('').layoutWeight(1)
Text(ip.status).fontSize(10).fontColor(ip.color)
}
.width('100%')
Text(ip.desc).fontSize(11).fontColor('#999999').margin({ top: 4 })
}
.layoutWeight(1)
.margin({ left: 12, bottom: 16 })
}
.width('100%')
}, (ip: CollabIP78) => ip.id.toString())
}
.width('100%').padding({ left: 16, right: 16 })
// 联名商品列表
Text('联名商品').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 8 })
Column() {
ForEach(COLLAB_ITEMS_78, (item: CollabItem78) => {
Row() {
Column() {
Text('🎨').fontSize(28)
}
.width(80).height(80)
.backgroundColor(item.color + '20')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(1).layoutWeight(1)
if (item.limited) {
Text('限量').fontSize(9).fontColor('#FFFFFF').backgroundColor('#FF5252').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%')
Text(item.ip).fontSize(10).fontColor('#AAAAAA').margin({ top: 2 })
Row() {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
Row() {
Text('★').fontSize(10).fontColor('#FFB300')
Text(item.rating.toString()).fontSize(10).fontColor('#FFB300').margin({ left: 2 })
Text(item.stock > 0 ? '库存' + item.stock : '已售罄').fontSize(9).fontColor(item.stock > 0 ? '#AAAAAA' : '#FF5252').margin({ left: 8 })
Text('').layoutWeight(1)
Text('¥').fontSize(12).fontColor('#FFB300')
Text(item.price.toString()).fontSize(16).fontColor('#FFB300').fontWeight(FontWeight.Bold)
Text('¥' + item.originalPrice).fontSize(10).fontColor('#CCCCCC').decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
}
.width('100%').margin({ top: 4 })
Button() {
Text(item.stock > 0 ? '立即购买' : '到货提醒').fontSize(11).fontColor('#FFFFFF')
}
.height(28).margin({ top: 6 })
.backgroundColor(item.stock > 0 ? item.color : '#BDBDBD')
.borderRadius(14)
.onClick(() => { if (item.stock > 0) { this.onBuy(item.id) } })
}
.margin({ left: 10 })
.layoutWeight(1)
}
.width('100%').padding(10).margin({ top: 8, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}, (item: CollabItem78) => item.id.toString())
}
.width('100%')
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
联名时间轴是本Tab的核心视觉创新。它通过Column容器纵向排列COLLAB_IPS_78数据,每一项由左侧的时间轴节点(Circle圆形+Line连接线)和右侧的内容信息组成。Circle组件的fill属性设置为IP颜色,实现了时间轴节点的彩色化。Line连接线使用layoutWeight(1)自动填充剩余高度,背景色为浅灰色#E0E0E0。通过idx < COLLAB_IPS_78.length - 1的条件判断,最后一个时间轴项不渲染连接线,保证了视觉的完整性。
联名商品列表的交互设计体现了库存感知能力。每个商品的按钮文字和背景色根据stock字段动态切换——库存大于0时显示"立即购买"并使用商品颜色作为背景,库存为0时显示"到货提醒"并使用灰色#BDBDBD背景。onClick事件也通过条件判断确保售罄商品不会触发购买流程。限量标签通过limited布尔字段控制显示,红色背景的"限量"标签有效地制造了稀缺感。
七、文具Tab与限时特价
文具Tab整合了文具分类网格、限时特价横条与文具商品列表三大板块,其中限时特价横条是该Tab的视觉亮点。
@Component
struct StationeryTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 文具分类网格
Text('文具分类').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Grid() {
ForEach(STATIONERY_CATS_78, (cat: StationeryCat78) => {
GridItem() {
Column() {
Text(cat.icon).fontSize(26)
Text(cat.label).fontSize(11).fontColor('#333333').margin({ top: 4 })
Text(cat.count + '件').fontSize(9).fontColor('#AAAAAA')
}
.width('100%').padding({ top: 10, bottom: 10 })
.backgroundColor(cat.bg)
.borderRadius(12)
.alignItems(HorizontalAlign.Center)
}
}, (cat: StationeryCat78) => cat.label)
}
.columnsTemplate('4fr 4fr 4fr 4fr')
.rowsGap(8).columnsGap(8)
.padding(16)
.height(210)
// 限时特价横条
Row() {
Column() {
Text('⚡ 限时特价').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('钢笔/墨水/手账 限时直降').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('距结束 02:18:35').fontSize(10).fontColor('#FFFFFF').backgroundColor('rgba(255,255,255,0.25)').padding({ left: 8, right: 8, top: 2, bottom: 2 }).borderRadius(10)
}
.width('100%').padding(12).margin({ left: 16, right: 16 })
.linearGradient({ angle: 90, colors: [['#FFB300', 0], ['#FF8F00', 1]] })
.borderRadius(12)
// 文具列表
Text('热门文具').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
ForEach(STATIONERY_ITEMS_78, (item: StationeryItem78) => {
Row() {
Column() {
Text(item.flashSale ? '⚡' : '✒️').fontSize(28)
}
.width(72).height(72)
.backgroundColor(item.color + '20')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(1).layoutWeight(1)
if (item.flashSale) {
Text('特价').fontSize(9).fontColor('#FFFFFF').backgroundColor('#FF5252').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%')
Text(item.brand).fontSize(10).fontColor('#AAAAAA').margin({ top: 2 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4, top: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
Row() {
Text('★').fontSize(10).fontColor('#FFB300')
Text(item.rating.toString()).fontSize(10).fontColor('#FFB300').margin({ left: 2 })
Text(item.sales + '人付款').fontSize(9).fontColor('#AAAAAA').margin({ left: 8 })
Text('').layoutWeight(1)
Text('¥').fontSize(12).fontColor('#FFB300')
Text(item.price.toString()).fontSize(16).fontColor('#FFB300').fontWeight(FontWeight.Bold)
Text('¥' + item.originalPrice).fontSize(10).fontColor('#CCCCCC').decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
}
.width('100%').margin({ top: 4 })
Button() {
Text('加入购物车').fontSize(11).fontColor('#FFFFFF')
}
.height(28).margin({ top: 6 })
.backgroundColor(item.color)
.borderRadius(14)
.onClick(() => { this.onBuy(item.id) })
}
.margin({ left: 10 })
.layoutWeight(1)
}
.width('100%').padding(10).margin({ top: 8, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}, (item: StationeryItem78) => item.id.toString())
}
.width('100%')
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
限时特价横条采用90度水平线性渐变(从#FFB300到#FF8F00),与文具Tab的其他墨青色调形成鲜明对比,有效地吸引了用户的视觉注意力。横条左侧展示"限时特价"标题与商品范围描述,右侧的倒计时标签使用半透明白色背景营造紧迫感,这种营销心理学驱动的视觉设计在电商场景中能够有效促进转化。
文具商品列表的图片占位区根据flashSale布尔字段动态切换图标——特价商品显示闪电图标⚡,普通商品显示钢笔图标✒️。特价商品在名称右侧追加红色"特价"标签,从视觉上与普通商品区分。商品信息行采用横向排列,将评分、销量、价格信息整合在一行内,配合layoutWeight(1)的弹性布局确保信息分布均匀。加入购物车按钮的文字为"加入购物车"而非"购买",更准确地反映了电商场景中的加入购物车操作语义。
八、个人中心与会员等级展示
个人中心Tab是用户数据管理的核心入口,集成了渐变头部、消费柱状图、会员等级、订单列表、地址管理与收藏列表六大功能模块。
@Component
struct ProfileTab78 {
onEditAddress: () => void = () => {}
onCancelOrder: (id: number) => void = () => {}
onDeleteFav: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 渐变头部
Column() {
Row() {
Column() {
Text('🎨').fontSize(40)
}
.width(64).height(64).borderRadius(32)
.backgroundColor('rgba(255,255,255,0.3)')
.justifyContent(FlexAlign.Center)
Column() {
Text('文创收藏家').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('ID: ART20260824').fontSize(11).fontColor('rgba(255,255,255,0.8)').margin({ top: 4 })
Row() {
Text('黄金会员').fontSize(10).fontColor('#FFB300').backgroundColor('rgba(255,193,7,0.25)').padding({ left: 6, right: 6, top: 1, bottom: 1 }).borderRadius(4)
Text('收藏 48件').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ left: 8 })
}
.margin({ top: 4 })
}
.margin({ left: 12 })
.layoutWeight(1)
}
.width('100%').padding(16)
}
.width('100%')
.linearGradient({ angle: 135, colors: [['#006064', 0], ['#004D40', 1]] })
// 消费柱状图
Text('近6月消费统计').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
Row() {
ForEach(BAR_DATA_78, (bar: BarData78) => {
Column() {
Text('¥' + bar.value).fontSize(8).fontColor('#999999')
Column() {
}
.width(20).height(bar.value / 12)
.backgroundColor(bar.color)
.borderRadius({ topLeft: 4, topRight: 4 })
Text(bar.label).fontSize(9).fontColor('#999999').margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
}, (bar: BarData78) => bar.label)
}
.width('100%').height(130)
.padding({ top: 8, bottom: 8 })
.alignItems(VerticalAlign.Bottom)
}
.width('100%').margin({ top: 8, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.padding(12)
// 会员等级
Text('会员等级').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Row() {
ForEach(MEMBER_LEVELS_78, (level: string, idx: number) => {
Column() {
Text(level).fontSize(10).fontColor(idx <= 2 ? '#FFB300' : '#CCCCCC')
Text(idx === 2 ? '当前' : '').fontSize(8).fontColor('#FFB300').margin({ top: 2 })
}
.layoutWeight(1).padding(8)
.backgroundColor(idx === 2 ? '#FFF8E1' : '#F5F5F5')
.borderRadius(8)
.margin({ right: 4 })
}, (level: string, idx: number) => idx.toString())
}
.width('100%').margin({ top: 8, left: 16, right: 16 })
// 订单列表
Row() {
Text('我的订单').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('全部 >').fontSize(11).fontColor('#999999')
}
.width('100%').padding({ left: 16, right: 16, top: 16 })
Column() {
ForEach(ORDERS_78, (order: OrderItem78) => {
Column() {
Row() {
Text(order.product).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(1).layoutWeight(1)
Text(order.status).fontSize(11).fontColor(order.color)
}
.width('100%')
Row() {
if (order.tracking.length > 0) {
Text('📦').fontSize(10)
Text(order.tracking).fontSize(10).fontColor('#AAAAAA').margin({ left: 2 })
} else {
Text('暂无物流').fontSize(10).fontColor('#AAAAAA')
}
Text('').layoutWeight(1)
Text('¥' + order.amount).fontSize(14).fontColor('#FFB300').fontWeight(FontWeight.Bold)
}
.width('100%').margin({ top: 4 })
Row() {
Text(order.date).fontSize(10).fontColor('#AAAAAA')
Text('').layoutWeight(1)
if (order.status === '待发货' || order.status === '预售中') {
Text('取消').fontSize(10).fontColor('#FF5252').onClick(() => { this.onCancelOrder(order.id) })
} else {
Text('查看物流').fontSize(10).fontColor('#006064')
}
}
.width('100%').margin({ top: 4 })
}
.width('100%').padding(12).margin({ top: 8, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(10)
.border({ width: 1, color: '#F0F0F0' })
}, (order: OrderItem78) => order.id.toString())
}
.width('100%')
// 地址管理
Row() {
Text('收货地址').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('编辑 >').fontSize(11).fontColor('#006064').onClick(() => { this.onEditAddress() })
}
.width('100%').padding({ left: 16, right: 16, top: 16 })
Column() {
ForEach(ADDRESSES_78, (addr: AddressItem78) => {
Row() {
Column() {
Text(addr.name + ' ' + addr.phone).fontSize(12).fontColor('#333333')
Text(addr.address).fontSize(10).fontColor('#AAAAAA').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (addr.isDefault) {
Text('默认').fontSize(9).fontColor('#006064').backgroundColor('#E0F7FA').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%').padding(12).margin({ top: 6, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(10)
.border({ width: 1, color: '#F0F0F0' })
}, (addr: AddressItem78) => addr.id.toString())
}
.width('100%')
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
个人中心头部的会员标签使用鎏金色#FFB300配合半透明黄色背景,与整体墨青色渐变形成对比,突出了会员等级的视觉权重。消费柱状图的height计算采用bar.value / 12的比例缩放,确保最大值(8月的1180元)映射为约98像素的柱子高度,整体图表高度控制在130像素以内。会员等级展示区域通过ForEach遍历MEMBER_LEVELS_78数组渲染五个等级,当前等级(黄金会员,索引2)以浅黄色#FFF8E1背景与"当前"文字标记,已达到的等级使用鎏金色文字,未达到的等级使用灰色文字。
订单列表的设计体现了电商场景的物流信息展示需求。每条订单包含商品名称、状态、物流单号、金额与日期信息。tracking字段通过条件判断显示——有物流单号时展示包裹图标与单号,无物流时显示"暂无物流"。操作按钮根据status值动态切换——"待发货"和"预售中"状态显示"取消"按钮,其他状态显示"查看物流"链接。地址管理区域通过isDefault布尔字段控制"默认"标签的显示,默认地址以浅青色背景的标签标识。
九、应用整体业务流程
十、取消订单与删除收藏弹窗
应用通过bindContentCover绑定了两个全屏覆盖确认弹窗——取消订单确认与删除收藏确认,采用了统一的弹窗视觉规范。
@Builder
buildCancelCover() {
Column() {
Column() {
Text('取消订单').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('确认取消此订单?取消后已支付的金额将原路退回').fontSize(13).fontColor('#999999').margin({ top: 12 }).textAlign(TextAlign.Center)
Row() {
Button() {
Text('再想想').fontSize(14).fontColor('#666666')
}
.layoutWeight(1).height(44)
.backgroundColor('#F5F5F5')
.borderRadius(22)
.margin({ right: 12 })
.onClick(() => { this.showCancelDialog = false })
Button() {
Text('确认取消').fontSize(14).fontColor('#FFFFFF')
}
.layoutWeight(1).height(44)
.backgroundColor('#FF5252')
.borderRadius(22)
.onClick(() => { this.showCancelDialog = false })
}
.width('100%').margin({ top: 24 })
}
.width('80%').padding(24)
.backgroundColor('#FFFFFF')
.borderRadius(20)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('rgba(0,0,0,0.5)')
}
@Builder
buildDeleteFavCover() {
Column() {
Column() {
Text('删除收藏').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('确认从收藏中移除这件文创?').fontSize(13).fontColor('#999999').margin({ top: 12 })
Row() {
Button() {
Text('取消').fontSize(14).fontColor('#666666')
}
.layoutWeight(1).height(44)
.backgroundColor('#F5F5F5')
.borderRadius(22)
.margin({ right: 12 })
.onClick(() => { this.showDeleteFavDialog = false })
Button() {
Text('删除').fontSize(14).fontColor('#FFFFFF')
}
.layoutWeight(1).height(44)
.backgroundColor('#FF5252')
.borderRadius(22)
.onClick(() => { this.showDeleteFavDialog = false })
}
.width('100%').margin({ top: 24 })
}
.width('80%').padding(24)
.backgroundColor('#FFFFFF')
.borderRadius(20)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('rgba(0,0,0,0.5)')
}
取消订单弹窗的提示文字"确认取消此订单?取消后已支付的金额将原路退回"包含了退款信息的说明,这种文案设计在电商场景中能够减少用户对取消操作后果的疑虑。删除收藏弹窗的提示文字"确认从收藏中移除这件文创?“使用了"这件文创"而非泛化的"此项目”,更贴合文创电商的业务语境。两个弹窗的按钮配色完全一致——取消/再想想按钮使用灰色背景与深灰文字,确认/删除按钮使用红色#FF5252背景与白色文字,通过色彩心理学引导用户谨慎执行破坏性操作。
十一、编辑地址弹窗
编辑地址弹窗提供了收货人姓名、手机号、详细地址的编辑功能,同时展示了已有地址列表供用户参考选择。
@Builder
buildEditAddressSheet() {
Column() {
Row() {
Text('编辑收货地址').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('✕').fontSize(20).fontColor('#999999').onClick(() => { this.showEditAddressDialog = false })
}
.width('100%').padding(16)
Scroll() {
Column() {
Text('收货人').fontSize(14).fontColor('#666666')
TextInput({ text: this.editAddressName, placeholder: '请输入收货人姓名' })
.width('100%').height(44).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.onChange((val: string) => { this.editAddressName = val })
Text('手机号').fontSize(14).fontColor('#666666').margin({ top: 12 })
TextInput({ text: this.editAddressPhone, placeholder: '请输入手机号' })
.width('100%').height(44).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.type(InputType.PhoneNumber)
.onChange((val: string) => { this.editAddressPhone = val })
Text('详细地址').fontSize(14).fontColor('#666666').margin({ top: 12 })
TextArea({ text: this.editAddressDetail, placeholder: '请输入详细收货地址' })
.width('100%').height(80).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.onChange((val: string) => { this.editAddressDetail = val })
Text('已有地址').fontSize(14).fontColor('#666666').margin({ top: 16 })
Column() {
ForEach(ADDRESSES_78, (addr: AddressItem78) => {
Row() {
Column() {
Text(addr.name + ' ' + addr.phone).fontSize(13).fontColor('#333333')
Text(addr.address).fontSize(11).fontColor('#AAAAAA').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (addr.isDefault) {
Text('默认').fontSize(9).fontColor('#006064').backgroundColor('#E0F7FA').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%').padding(10).margin({ top: 6 })
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#F0F0F0' })
}, (addr: AddressItem78) => addr.id.toString())
}
.width('100%')
}
.width('100%').padding({ left: 16, right: 16, bottom: 16 })
}
.constraintSize({ maxHeight: '50%' })
Row() {
Button() {
Text('保存地址').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
}
.layoutWeight(1).height(48)
.backgroundColor('#006064')
.borderRadius(24)
.onClick(() => { this.showEditAddressDialog = false })
}
.width('100%').padding(16)
}
.width('100%')
}
编辑地址弹窗的表单设计体现了移动端地址录入的交互规范。手机号输入框使用InputType.PhoneNumber类型,在移动设备上会自动调起数字键盘,提升了输入效率。详细地址使用TextArea组件而非TextInput,高度设为80以支持多行地址文本的输入。已有地址列表通过ForEach遍历ADDRESSES_78数据渲染,默认地址在右侧显示浅青色"默认"标签。Scroll组件的maxHeight设为50%,确保表单内容过多时仍可滚动且底部保存按钮始终可见。
技术点对比总结
| 技术维度 | 国潮Tab | 联名Tab | 文具Tab | 手账Tab | 社区Tab | 个人中心 |
|---|---|---|---|---|---|---|
| 布局方式 | Flex标签云+大卡+双列Grid | 横滑+时间轴+列表 | Grid四列+特价横条+列表 | 横滑+标签云+列表 | 横滑+列表 | 柱状图+等级+列表 |
| 数据驱动 | GUOCHAO_TAGS+CULTURAL_ITEMS | COLLAB_IPS+COLLAB_ITEMS | STATIONERY_CATS+ITEMS | TEMPLATES+POSTS+TAGS | TOPICS+POSTS | BAR_DATA+ORDERS+ADDRESSES+FAVORITES |
| 交互回调 | onBuy购买 | onBuy购买 | onBuy购买 | onBuy购买 | 无操作回调 | onEditAddress/onCancel/onDelete |
| 视觉特色 | 渐变大卡+标签云边框 | Circle时间轴+库存感知 | 限时特价渐变横条 | 模板热度标记+社区标签 | 话题横滑+动态图片网格 | 会员等级色彩编码+物流信息 |
| 状态管理 | 无独立状态 | 无独立状态 | 无独立状态 | 无独立状态 | 无独立状态 | 通过回调触发父组件状态 |
| 弹窗触发 | 购买弹窗 | 购买弹窗 | 购买弹窗 | 购买弹窗 | 无弹窗 | 编辑地址/取消/删除弹窗 |
| 滚动方式 | Vertical | Vertical+Horizontal | Vertical | Vertical+Horizontal | Vertical+Horizontal | Vertical |
安装DevEco Studio程序

选择目标安装目录:

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

新建一个空白模板:

设置API为24的模板项目:

初始化项目,自动下载相关依赖:

完整代码:
// 主题:文创墨青 #006064 × 鎏金 #FFB300,底色 #F0F7FA(清雅水墨调)
// 布局差异:国潮分类+精选大卡+双列文创 / 联名IP横滑+联名列表+时间轴 / 文具网格+特价横条+文具列表 / 手账模板+手账社区+热门标签 / 文创动态+热门话题 / 渐变头+消费柱状图+会员等级+订单+收藏
// ============ 类型定义 ============
interface CulturalItem78 {
id: number
name: string
price: number
originalPrice: number
origin: string
material: string
rating: number
sales: number
tags: string[]
color: string
desc: string
}
interface GuochaoTag78 {
label: string
color: string
count: number
}
interface CollabIP78 {
id: number
name: string
brand: string
date: string
items: number
status: string
color: string
desc: string
}
interface CollabItem78 {
id: number
name: string
ip: string
price: number
originalPrice: number
stock: number
limited: boolean
color: string
rating: number
tags: string[]
}
interface StationeryCat78 {
label: string
icon: string
color: string
bg: string
count: number
}
interface StationeryItem78 {
id: number
name: string
brand: string
price: number
originalPrice: number
rating: number
sales: number
tags: string[]
color: string
flashSale: boolean
}
interface JournalTemplate78 {
id: number
name: string
style: string
price: number
pages: number
color: string
popular: boolean
}
interface JournalPost78 {
id: number
author: string
template: string
content: string
likes: number
comments: number
images: number
timeAgo: string
tags: string[]
avatarColor: string
}
interface CommunityPost78 {
id: number
author: string
avatarColor: string
content: string
likes: number
comments: number
shares: number
images: number
timeAgo: string
topic: string
}
interface TopicItem78 {
id: number
title: string
posts: number
hot: boolean
color: string
}
interface OrderItem78 {
id: number
product: string
date: string
amount: number
status: string
tracking: string
color: string
}
interface FavoriteItem78 {
id: number
name: string
price: number
type: string
color: string
}
interface AddressItem78 {
id: number
name: string
phone: string
address: string
isDefault: boolean
tag: string
}
interface BarData78 {
label: string
value: number
color: string
}
interface SpecOption78 {
id: number
name: string
price: number
color: string
}
// ============ 静态数据 ============
const GUOCHAO_TAGS_78: GuochaoTag78[] = [
{ label: '故宫文创', color: '#8D6E63', count: 128 },
{ label: '敦煌飞天', color: '#FFB300', count: 96 },
{ label: '青花瓷', color: '#1565C0', count: 85 },
{ label: '十二生肖', color: '#C2185B', count: 72 },
{ label: '山海经', color: '#006064', count: 64 },
{ label: '二十四节气', color: '#2E7D32', count: 58 }
]
const CULTURAL_ITEMS_78: CulturalItem78[] = [
{ id: 1, name: '故宫千里江山书签套装', price: 38, originalPrice: 58, origin: '故宫博物院', material: '黄铜+丝绸', rating: 4.9, sales: 8542, tags: ['爆款', '限量'], color: '#006064', desc: '千里江山图复刻书签,丝绸流苏' },
{ id: 2, name: '敦煌飞天手账本', price: 48, originalPrice: 68, origin: '敦煌博物馆', material: '特种纸', rating: 4.8, sales: 6235, tags: ['热销'], color: '#FFB300', desc: '莫高窟壁画封面,内页4种格式' },
{ id: 3, name: '青花瓷纹胶带套装', price: 28, originalPrice: 42, origin: '景德镇', material: '和纸胶带', rating: 4.7, sales: 4856, tags: ['手账必备'], color: '#1565C0', desc: '6卷青花瓷纹样,宽窄各3卷' },
{ id: 4, name: '十二生肖金属徽章', price: 12, originalPrice: 18, origin: '国博文创', material: '珐琅', rating: 4.9, sales: 12653, tags: ['盲盒', '收藏'], color: '#C2185B', desc: '可选生肖,金属珐琅工艺' },
{ id: 5, name: '山海经异兽插画明信片', price: 22, originalPrice: 35, origin: '原创设计', material: '特种纸', rating: 4.8, sales: 3567, tags: ['原创'], color: '#006064', desc: '12张异兽插画,烫金工艺' },
{ id: 6, name: '二十四节气印章套装', price: 68, originalPrice: 98, origin: '非遗文创', material: '石质', rating: 4.9, sales: 2876, tags: ['非遗', '礼盒'], color: '#2E7D32', desc: '24枚节气印章+印泥套装' },
{ id: 7, name: '千里江山丝巾', price: 128, originalPrice: 188, origin: '故宫文创', material: '桑蚕丝', rating: 5.0, sales: 1542, tags: ['高端', '礼赠'], color: '#006064', desc: '90×90cm真丝,千里江山图案' },
{ id: 8, name: '诗词折扇(竹骨)', price: 45, originalPrice: 68, origin: '苏州', material: '竹+宣纸', rating: 4.7, sales: 3654, tags: ['夏日'], color: '#8D6E63', desc: '手绘水墨诗词,14骨竹扇' },
{ id: 9, name: '国风灯笼摆件', price: 88, originalPrice: 128, origin: '原创设计', material: '竹+纸', rating: 4.8, sales: 2145, tags: ['装饰'], color: '#FF6F00', desc: '可折叠纸灯笼,含LED灯' },
{ id: 10, name: '青花瓷纹杯子', price: 58, originalPrice: 88, origin: '景德镇', material: '骨瓷', rating: 4.9, sales: 5432, tags: ['日用'], color: '#1565C0', desc: '300ml马克杯,青花缠枝纹' }
]
const COLLAB_IPS_78: CollabIP78[] = [
{ id: 1, name: '故宫×三星堆', brand: '故宫文创', date: '2026-08', items: 28, status: '热销中', color: '#8D6E63', desc: '青铜面具×故宫纹样联名系列' },
{ id: 2, name: '大英博物馆×国博', brand: '国家博物馆', date: '2026-07', items: 15, status: '热销中', color: '#006064', desc: '东西方文明碰撞联名系列' },
{ id: 3, name: '敦煌×星巴克', brand: '敦煌博物馆', date: '2026-09', items: 12, status: '即将发售', color: '#FFB300', desc: '飞天纹样限量杯具联名' },
{ id: 4, name: '国博×泡泡玛特', brand: '泡泡玛特', date: '2026-06', items: 8, status: '售罄', color: '#C2185B', desc: '文物盲盒系列,已售罄' },
{ id: 5, name: '三星堆×MOLLY', brand: '三星堆博物馆', date: '2026-09', items: 10, status: '预售中', color: '#1565C0', desc: '青铜面具MOLLY联名手办' },
{ id: 6, name: '故宫×乐高', brand: '乐高', date: '2026-10', items: 5, status: '即将发售', color: '#00897B', desc: '太和殿积木联名套装' }
]
const COLLAB_ITEMS_78: CollabItem78[] = [
{ id: 1, name: '三星堆青铜面具徽章', ip: '故宫×三星堆', price: 25, originalPrice: 38, stock: 356, limited: true, color: '#8D6E63', rating: 4.9, tags: ['限量', '联名'] },
{ id: 2, name: '故宫三星堆联名手账', ip: '故宫×三星堆', price: 58, originalPrice: 88, stock: 128, limited: true, color: '#006064', rating: 4.8, tags: ['限量', '手账'] },
{ id: 3, name: '大英博物馆胶带套装', ip: '大英博物馆×国博', price: 35, originalPrice: 52, stock: 678, limited: false, color: '#006064', rating: 4.7, tags: ['联名'] },
{ id: 4, name: '敦煌飞天限量马克杯', ip: '敦煌×星巴克', price: 88, originalPrice: 128, stock: 0, limited: true, color: '#FFB300', rating: 5.0, tags: ['预售', '限量'] },
{ id: 5, name: '国博文物盲盒', ip: '国博×泡泡玛特', price: 69, originalPrice: 99, stock: 0, limited: true, color: '#C2185B', rating: 4.9, tags: ['盲盒', '售罄'] },
{ id: 6, name: '三星堆MOLLY手办', ip: '三星堆×MOLLY', price: 128, originalPrice: 168, stock: 89, limited: true, color: '#1565C0', rating: 4.8, tags: ['预售', '限量'] },
{ id: 7, name: '故宫乐高太和殿', ip: '故宫×乐高', price: 588, originalPrice: 698, stock: 45, limited: true, color: '#00897B', rating: 5.0, tags: ['高端', '预售'] },
{ id: 8, name: '联名明信片套装', ip: '故宫×三星堆', price: 18, originalPrice: 28, stock: 854, limited: false, color: '#8D6E63', rating: 4.6, tags: ['联名'] }
]
const STATIONERY_CATS_78: StationeryCat78[] = [
{ label: '钢笔', icon: '✒️', color: '#006064', bg: '#E0F7FA', count: 128 },
{ label: '墨水', icon: '💧', color: '#1565C0', bg: '#E3F2FD', count: 96 },
{ label: '笔记本', icon: '📓', color: '#FFB300', bg: '#FFF8E1', count: 85 },
{ label: '胶带', icon: '🎨', color: '#C2185B', bg: '#FCE4EC', count: 72 },
{ label: '印章', icon: '🔖', color: '#2E7D32', bg: '#E8F5E9', count: 64 },
{ label: '贴纸', icon: '🌸', color: '#6A1B9A', bg: '#F3E5F5', count: 58 },
{ label: '尺规', icon: '📐', color: '#00897B', bg: '#E0F2F1', count: 45 },
{ label: '收纳', icon: '📦', color: '#E65100', bg: '#FFF3E0', count: 38 }
]
const STATIONERY_ITEMS_78: StationeryItem78[] = [
{ id: 1, name: '凌美Safari钢笔(国风限定)', brand: 'LAMY', price: 168, originalPrice: 228, rating: 4.9, sales: 3542, tags: ['限定', '国风'], color: '#006064', flashSale: true },
{ id: 2, name: '百乐色彩雫墨水30ml', brand: 'PILOT', price: 45, originalPrice: 68, rating: 4.8, sales: 5234, tags: ['热销'], color: '#1565C0', flashSale: false },
{ id: 3, name: '国博联名手账本A5', brand: '国博文创', price: 58, originalPrice: 88, rating: 4.8, sales: 4156, tags: ['联名'], color: '#FFB300', flashSale: true },
{ id: 4, name: '青花瓷纹胶带6卷装', brand: '景德镇', price: 28, originalPrice: 42, rating: 4.7, sales: 6854, tags: ['热销'], color: '#1565C0', flashSale: false },
{ id: 5, name: '二十四节气印章套装', brand: '非遗文创', price: 68, originalPrice: 98, rating: 4.9, sales: 2876, tags: ['礼盒'], color: '#2E7D32', flashSale: true },
{ id: 6, name: '古风手账贴纸100枚', brand: '原创设计', price: 15, originalPrice: 25, rating: 4.6, sales: 8543, tags: ['平价'], color: '#6A1B9A', flashSale: false },
{ id: 7, name: '黄铜直尺30cm(国风)', brand: '文创工坊', price: 35, originalPrice: 52, rating: 4.7, sales: 1654, tags: ['黄铜'], color: '#00897B', flashSale: false },
{ id: 8, name: '桌面收纳盒(竹制)', brand: '手作工坊', price: 88, originalPrice: 128, rating: 4.8, sales: 1235, tags: ['竹制', '收纳'], color: '#E65100', flashSale: true },
{ id: 9, name: '书法练习套装', brand: '墨韵堂', price: 128, originalPrice: 188, rating: 4.9, sales: 2354, tags: ['套装'], color: '#8D6E63', flashSale: false },
{ id: 10, name: '复古信封信纸套装', brand: '原创设计', price: 22, originalPrice: 35, rating: 4.7, sales: 3456, tags: ['复古'], color: '#5D4037', flashSale: false },
{ id: 11, name: '彩色中性笔10支装', brand: '斑马', price: 32, originalPrice: 48, rating: 4.8, sales: 7654, tags: ['热销'], color: '#C2185B', flashSale: true },
{ id: 12, name: '国风书签金属套装', brand: '文创工坊', price: 38, originalPrice: 58, rating: 4.8, sales: 4567, tags: ['套装', '金属'], color: '#006064', flashSale: false }
]
const JOURNAL_TEMPLATES_78: JournalTemplate78[] = [
{ id: 1, name: '水墨丹青', style: '中国风', price: 58, pages: 160, color: '#006064', popular: true },
{ id: 2, name: '敦煌飞天', style: '壁画风', price: 68, pages: 200, color: '#FFB300', popular: true },
{ id: 3, name: '青花瓷韵', style: '青花风', price: 48, pages: 120, color: '#1565C0', popular: false },
{ id: 4, name: '山海经异', style: '神兽风', price: 78, pages: 180, color: '#2E7D32', popular: true },
{ id: 5, name: '二十四节气', style: '节气风', price: 88, pages: 240, color: '#8D6E63', popular: false },
{ id: 6, name: '十二生肖', style: '生肖风', price: 52, pages: 144, color: '#C2185B', popular: true }
]
const JOURNAL_POSTS_78: JournalPost78[] = [
{ id: 1, author: '墨韵小主', template: '水墨丹青', content: '用水墨丹青模板做了一整月的记录,配上自制的印章太有仪式感了!分享几个排版小技巧~', likes: 456, comments: 89, images: 4, timeAgo: '3小时前', tags: ['#排版技巧', '#水墨风'], avatarColor: '#006064' },
{ id: 2, author: '飞天少女', template: '敦煌飞天', content: '敦煌飞天的封面太美了!配上飞天纹样的胶带和贴纸,手账瞬间高级感拉满~', likes: 678, comments: 123, images: 5, timeAgo: '6小时前', tags: ['#敦煌风', '#胶带分享'], avatarColor: '#FFB300' },
{ id: 3, author: '青花姐姐', template: '青花瓷韵', content: '青花瓷的配色太优雅了,用水蓝色系文具搭配,做出来的周计划简直是艺术品!', likes: 534, comments: 98, images: 3, timeAgo: '12小时前', tags: ['#周计划', '#青花瓷'], avatarColor: '#1565C0' },
{ id: 4, author: '山海异客', template: '山海经异', content: '用山海经模板画了整本异兽图鉴,搭配金属徽章做装饰,效果绝了!', likes: 892, comments: 156, images: 6, timeAgo: '1天前', tags: ['#异兽图鉴', '#金属徽章'], avatarColor: '#2E7D32' },
{ id: 5, author: '节气记录者', template: '二十四节气', content: '每个节气都记录了当天的花信和物候,一整年下来做成了一本自然观察日记~', likes: 745, comments: 134, images: 4, timeAgo: '2天前', tags: ['#自然观察', '#节气'], avatarColor: '#8D6E63' }
]
const POSTS_78: CommunityPost78[] = [
{ id: 1, author: '文创收集控', avatarColor: '#006064', content: '终于集齐了故宫×三星堆联名系列全套!青铜面具徽章搭配千里江山书签,国风满满~', likes: 568, comments: 89, shares: 34, images: 5, timeAgo: '2小时前', topic: '#联名收藏#' },
{ id: 2, author: '手账达人小Q', avatarColor: '#FFB300', content: '分享本周手账排版:用水墨丹青模板+青花瓷胶带+节气印章,效果太满意了!附详细步骤', likes: 892, comments: 156, shares: 67, images: 6, timeAgo: '5小时前', topic: '#手账排版#' },
{ id: 3, author: '书法爱好者', avatarColor: '#1565C0', content: '入手了凌美国风限定钢笔,搭配百利色彩雫墨水,书写体验满分!附开箱测评', likes: 434, comments: 78, shares: 23, images: 4, timeAgo: '8小时前', topic: '#文具测评#' },
{ id: 4, author: '敦煌迷妹', avatarColor: '#C2185B', content: '敦煌×星巴克联名杯终于到货了!飞天纹样的马克杯质感超好,国潮联名越来越卷了', likes: 1234, comments: 234, shares: 89, images: 3, timeAgo: '12小时前', topic: '#联名开箱#' },
{ id: 5, author: '非遗传承人', avatarColor: '#2E7D32', content: '二十四节气印章套装评测:每枚印章的纹样都经过考证,盖出来的效果很有文化底蕴', likes: 678, comments: 123, shares: 45, images: 5, timeAgo: '1天前', topic: '#文创测评#' },
{ id: 6, author: '国风设计师', avatarColor: '#8D6E63', content: '分享几款原创国风设计:从山海经到青花瓷,传统文化元素怎么用才不俗气?', likes: 945, comments: 187, shares: 78, images: 4, timeAgo: '2天前', topic: '#设计分享#' }
]
const TOPICS_78: TopicItem78[] = [
{ id: 1, title: '故宫联名全收集', posts: 2345, hot: true, color: '#8D6E63' },
{ id: 2, title: '手账排版大赛', posts: 1876, hot: true, color: '#FFB300' },
{ id: 3, title: '国风文具推荐', posts: 1562, hot: true, color: '#006064' },
{ id: 4, title: '敦煌文创开箱', posts: 854, hot: false, color: '#FFB300' },
{ id: 5, title: '非遗手作分享', posts: 654, hot: false, color: '#2E7D32' },
{ id: 6, title: '联名盲盒交换', posts: 432, hot: false, color: '#C2185B' }
]
const ORDERS_78: OrderItem78[] = [
{ id: 1, product: '故宫千里江山书签套装', date: '08-22', amount: 38, status: '已签收', tracking: 'SF1234567890', color: '#4CAF50' },
{ id: 2, product: '敦煌飞天手账本', date: '08-18', amount: 48, status: '已签收', tracking: 'YT0987654321', color: '#4CAF50' },
{ id: 3, product: '三星堆青铜面具徽章', date: '08-28', amount: 25, status: '运输中', tracking: 'ZT5678901234', color: '#FF9800' },
{ id: 4, product: '凌美Safari钢笔限定', date: '09-01', amount: 168, status: '待发货', tracking: '', color: '#2196F3' },
{ id: 5, product: '二十四节气印章套装', date: '09-03', amount: 68, status: '待发货', tracking: '', color: '#2196F3' },
{ id: 6, product: '敦煌×星巴克联名杯', date: '09-05', amount: 88, status: '预售中', tracking: '', color: '#9C27B0' }
]
const FAVORITES_78: FavoriteItem78[] = [
{ id: 1, name: '千里江山丝巾', price: 128, type: '国潮文创', color: '#006064' },
{ id: 2, name: '故宫×乐高太和殿', price: 588, type: '联名周边', color: '#00897B' },
{ id: 3, name: '敦煌飞天手账本', price: 48, type: '国潮文创', color: '#FFB300' },
{ id: 4, name: '二十四节气印章', price: 68, type: '文房用品', color: '#2E7D32' },
{ id: 5, name: '凌美国风限定钢笔', price: 168, type: '文房用品', color: '#1565C0' }
]
const ADDRESSES_78: AddressItem78[] = [
{ id: 1, name: '张文创', phone: '138****8888', address: '北京市朝阳区建国路88号文创大厦A座2001室', isDefault: true, tag: '公司' },
{ id: 2, name: '张文创', phone: '138****8888', address: '北京市海淀区中关村大街12号院3号楼502室', isDefault: false, tag: '家' }
]
const BAR_DATA_78: BarData78[] = [
{ label: '3月', value: 380, color: '#4DB6AC' },
{ label: '4月', value: 520, color: '#4DB6AC' },
{ label: '5月', value: 450, color: '#4DB6AC' },
{ label: '6月', value: 680, color: '#4DB6AC' },
{ label: '7月', value: 820, color: '#4DB6AC' },
{ label: '8月', value: 1180, color: '#006064' }
]
const SPEC_OPTIONS_78: SpecOption78[] = [
{ id: 1, name: '标准版', price: 0, color: '#006064' },
{ id: 2, name: '礼盒版', price: 20, color: '#FFB300' },
{ id: 3, name: '限定版', price: 50, color: '#C2185B' }
]
const MEMBER_LEVELS_78: string[] = ['青铜会员', '白银会员', '黄金会员', '铂金会员', '钻石会员']
// ============ 主入口组件 ============
@Entry
@Component
struct DuoDuoCulturalApp {
@State currentTab: number = 0
@State showBuyDialog: boolean = false
@State showCancelDialog: boolean = false
@State showEditAddressDialog: boolean = false
@State showDeleteFavDialog: boolean = false
@State selectedItemId: number = 0
@State selectedSpecId: number = 1
@State buyQuantity: number = 1
@State deliveryMethod: number = 0
@State buyNotes: string = ''
@State editAddressName: string = ''
@State editAddressPhone: string = ''
@State editAddressDetail: string = ''
@State cancelTargetId: number = 0
private tabs: string[] = ['国潮', '联名', '文具', '手账', '社区', '我的']
private deliveryOptions: string[] = ['普通配送 ¥5', '顺丰速运 ¥12', '同城急送 ¥8']
build() {
Column() {
// ====== 顶部头部 ======
Column() {
Row() {
Column() {
Text('多多文创')
.fontSize(22)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Text('国潮·联名·手账精选')
.fontSize(11)
.fontColor('rgba(255,255,255,0.8)')
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Row() {
Text('🛒').fontSize(18).fontColor('#FFFFFF')
}
.width(36).height(36).justifyContent(FlexAlign.Center)
.backgroundColor('rgba(255,255,255,0.2)')
.borderRadius(18)
}
.width('100%').height(56).padding({ left: 16, right: 16 })
.alignItems(VerticalAlign.Center)
Row() {
Text('搜"故宫文创""敦煌联名"...')
.fontSize(13)
.fontColor('rgba(255,255,255,0.6)')
.layoutWeight(1)
Text('🔍').fontSize(16).fontColor('rgba(255,255,255,0.6)')
}
.width('100%').height(36).margin({ top: 4 })
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(255,255,255,0.15)')
.borderRadius(20)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.linearGradient({ angle: 135, colors: [['#006064', 0], ['#004D40', 1]] })
.padding({ top: 8, bottom: 12, left: 16, right: 16 })
// ====== Tab内容区 ======
Stack()
{
Column() {
if (this.currentTab === 0) {
GuochaoTab78({
onBuy: (id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
}
})
}
if (this.currentTab === 1) {
CollabTab78({
onBuy: ((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 2) {
StationeryTab78({
onBuy:((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 3) {
JournalTab78({
onBuy:((id: number) => {
this.selectedItemId = id
this.showBuyDialog = true
})
})
}
if (this.currentTab === 4) {
CommunityTab78()
}
if (this.currentTab === 5) {
ProfileTab78({
onEditAddress: () => {
this.showEditAddressDialog = true
},
onCancelOrder: (id: number) => {
this.cancelTargetId = id
this.showCancelDialog = true
},
onDeleteFav: (id: number) => {
this.cancelTargetId = id
this.showDeleteFavDialog = true
}
})
}
}
.width('100%').height('100%')
}
// ====== 底部Tab栏 ======
Row() {
ForEach(this.tabs, (tab: string, idx: number) => {
Column() {
Text(this.getTabIcon(idx))
.fontSize(20)
.fontColor(this.currentTab === idx ? '#006064' : '#999999')
Text(tab)
.fontSize(10)
.fontColor(this.currentTab === idx ? '#006064' : '#999999')
.margin({ top: 2 })
}
.layoutWeight(1)
.height(56)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentTab = idx
})
}, (tab: string) => tab)
}
.width('100%')
.height(56)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
}
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
.bindContentCover(this.showCancelDialog, this.buildCancelCover())
.bindContentCover(this.showDeleteFavDialog, this.buildDeleteFavCover())
}
private getTabIcon(idx: number): string {
const icons: string[] = ['🏮', '🤝', '✒️', '📓', '💬', '👤']
return idx < icons.length ? icons[idx] : '📋'
}
@Builder
buildBuySheet() {
Column() {
Row() {
Text('购买文创').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('✕').fontSize(20).fontColor('#999999').onClick(() => { this.showBuyDialog = false })
}
.width('100%').padding(16)
Scroll() {
Column() {
// 规格选择
Text('选择规格').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
Row() {
ForEach(SPEC_OPTIONS_78, (spec: SpecOption78) => {
Column() {
Text(spec.name).fontSize(12).fontColor(this.selectedSpecId === spec.id ? '#FFFFFF' : '#666666')
if (spec.price > 0) {
Text('+' + spec.price + '元').fontSize(9).fontColor(this.selectedSpecId === spec.id ? 'rgba(255,255,255,0.8)' : '#AAAAAA').margin({ top: 2 })
} else {
Text('无加价').fontSize(9).fontColor(this.selectedSpecId === spec.id ? 'rgba(255,255,255,0.8)' : '#AAAAAA').margin({ top: 2 })
}
}
.layoutWeight(1).padding(10)
.borderRadius(10)
.backgroundColor(this.selectedSpecId === spec.id ? spec.color : '#F5F5F5')
.margin({ right: 8 })
.onClick(() => { this.selectedSpecId = spec.id })
}, (spec: SpecOption78) => spec.id.toString())
}
.width('100%').margin({ top: 8 })
// 配送方式
Text('配送方式').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ top: 16 })
Column() {
ForEach(this.deliveryOptions, (opt: string, idx: number) => {
Row() {
Radio({ value: idx.toString(), group: 'delivery' })
.checked(this.deliveryMethod === idx)
.onChange((checked: boolean) => { if (checked) { this.deliveryMethod = idx } })
Text(opt).fontSize(12).fontColor('#333333').margin({ left: 8 })
Text('').layoutWeight(1)
Text(this.deliveryMethod === idx ? '✓' : '').fontSize(14).fontColor('#006064')
}
.width('100%').padding(10)
.margin({ top: 4 })
.borderRadius(8)
.backgroundColor(this.deliveryMethod === idx ? '#E0F7FA' : '#F5F5F5')
.onClick(() => { this.deliveryMethod = idx })
}, (opt: string, idx: number) => idx.toString())
}
.width('100%')
// 数量
Row() {
Text('购买数量').fontSize(14).fontColor('#333333')
Text('').layoutWeight(1)
Row() {
Button() {
Text('-').fontSize(16).fontColor('#666666')
}
.width(32).height(32)
.backgroundColor('#F5F5F5')
.borderRadius(16)
.onClick(() => { if (this.buyQuantity > 1) { this.buyQuantity-- } })
Text(this.buyQuantity.toString()).fontSize(14).fontColor('#333333').width(40).textAlign(TextAlign.Center)
Button() {
Text('+').fontSize(16).fontColor('#666666')
}
.width(32).height(32)
.backgroundColor('#F5F5F5')
.borderRadius(16)
.onClick(() => { this.buyQuantity++ })
}
}
.width('100%').margin({ top: 16 })
// 备注
Text('备注').fontSize(14).fontColor('#333333').margin({ top: 16 })
TextArea({ text: this.buyNotes, placeholder: '如有定制需求请备注...' })
.width('100%').height(60)
.margin({ top: 8 })
.borderRadius(10)
.backgroundColor('#F5F5F5')
.onChange((val: string) => { this.buyNotes = val })
// 价格汇总
Row() {
Text('合计').fontSize(13).fontColor('#666666')
Text('').layoutWeight(1)
Text('¥' + this.getTotalPrice()).fontSize(20).fontColor('#FFB300').fontWeight(FontWeight.Bold)
}
.width('100%').margin({ top: 20, bottom: 12 })
}
.width('100%').padding({ left: 16, right: 16, bottom: 16 })
}
.constraintSize({ maxHeight: '55%' })
Row() {
Button() {
Text('确认下单').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
}
.layoutWeight(1).height(48)
.backgroundColor('#006064')
.borderRadius(24)
.onClick(() => { this.showBuyDialog = false })
}
.width('100%').padding(16)
}
.width('100%')
}
@Builder
buildCancelCover() {
Column() {
Column() {
Text('取消订单').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('确认取消此订单?取消后已支付的金额将原路退回').fontSize(13).fontColor('#999999').margin({ top: 12 }).textAlign(TextAlign.Center)
Row() {
Button() {
Text('再想想').fontSize(14).fontColor('#666666')
}
.layoutWeight(1).height(44)
.backgroundColor('#F5F5F5')
.borderRadius(22)
.margin({ right: 12 })
.onClick(() => { this.showCancelDialog = false })
Button() {
Text('确认取消').fontSize(14).fontColor('#FFFFFF')
}
.layoutWeight(1).height(44)
.backgroundColor('#FF5252')
.borderRadius(22)
.onClick(() => { this.showCancelDialog = false })
}
.width('100%').margin({ top: 24 })
}
.width('80%').padding(24)
.backgroundColor('#FFFFFF')
.borderRadius(20)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('rgba(0,0,0,0.5)')
}
@Builder
buildEditAddressSheet() {
Column() {
Row() {
Text('编辑收货地址').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('').layoutWeight(1)
Text('✕').fontSize(20).fontColor('#999999').onClick(() => { this.showEditAddressDialog = false })
}
.width('100%').padding(16)
Scroll() {
Column() {
Text('收货人').fontSize(14).fontColor('#666666')
TextInput({ text: this.editAddressName, placeholder: '请输入收货人姓名' })
.width('100%').height(44).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.onChange((val: string) => { this.editAddressName = val })
Text('手机号').fontSize(14).fontColor('#666666').margin({ top: 12 })
TextInput({ text: this.editAddressPhone, placeholder: '请输入手机号' })
.width('100%').height(44).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.type(InputType.PhoneNumber)
.onChange((val: string) => { this.editAddressPhone = val })
Text('详细地址').fontSize(14).fontColor('#666666').margin({ top: 12 })
TextArea({ text: this.editAddressDetail, placeholder: '请输入详细收货地址' })
.width('100%').height(80).margin({ top: 8 })
.borderRadius(10).backgroundColor('#F5F5F5')
.onChange((val: string) => { this.editAddressDetail = val })
Text('已有地址').fontSize(14).fontColor('#666666').margin({ top: 16 })
Column() {
ForEach(ADDRESSES_78, (addr: AddressItem78) => {
Row() {
Column() {
Text(addr.name + ' ' + addr.phone).fontSize(13).fontColor('#333333')
Text(addr.address).fontSize(11).fontColor('#AAAAAA').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (addr.isDefault) {
Text('默认').fontSize(9).fontColor('#006064').backgroundColor('#E0F7FA').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%').padding(10).margin({ top: 6 })
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#F0F0F0' })
}, (addr: AddressItem78) => addr.id.toString())
}
.width('100%')
}
.width('100%').padding({ left: 16, right: 16, bottom: 16 })
}
.constraintSize({ maxHeight: '50%' })
Row() {
Button() {
Text('保存地址').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
}
.layoutWeight(1).height(48)
.backgroundColor('#006064')
.borderRadius(24)
.onClick(() => { this.showEditAddressDialog = false })
}
.width('100%').padding(16)
}
.width('100%')
}
@Builder
buildDeleteFavCover() {
Column() {
Column() {
Text('删除收藏').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('确认从收藏中移除这件文创?').fontSize(13).fontColor('#999999').margin({ top: 12 })
Row() {
Button() {
Text('取消').fontSize(14).fontColor('#666666')
}
.layoutWeight(1).height(44)
.backgroundColor('#F5F5F5')
.borderRadius(22)
.margin({ right: 12 })
.onClick(() => { this.showDeleteFavDialog = false })
Button() {
Text('删除').fontSize(14).fontColor('#FFFFFF')
}
.layoutWeight(1).height(44)
.backgroundColor('#FF5252')
.borderRadius(22)
.onClick(() => { this.showDeleteFavDialog = false })
}
.width('100%').margin({ top: 24 })
}
.width('80%').padding(24)
.backgroundColor('#FFFFFF')
.borderRadius(20)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('rgba(0,0,0,0.5)')
}
private getTotalPrice(): number {
let spec: SpecOption78 | undefined = SPEC_OPTIONS_78.find((s: SpecOption78) => s.id === this.selectedSpecId)
let specPrice: number = spec ? spec.price : 0
let deliveryPrice: number = 5
if (this.deliveryMethod === 1) { deliveryPrice = 12 }
if (this.deliveryMethod === 2) { deliveryPrice = 8 }
return (38 + specPrice) * this.buyQuantity + deliveryPrice
}
}
// ============ 国潮Tab ============
@Component
struct GuochaoTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 国潮分类标签
Text('国潮分类').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(GUOCHAO_TAGS_78, (tag: GuochaoTag78) => {
Row() {
Text(tag.label).fontSize(12).fontColor(tag.color)
Text(tag.count + '件').fontSize(9).fontColor('#AAAAAA').margin({ left: 4 })
}
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.backgroundColor(tag.color + '15')
.borderRadius(16)
.border({ width: 1, color: tag.color + '30' })
}, (tag: GuochaoTag78) => tag.label)
}
.width('100%').padding({ left: 16, right: 16 })
// 精选大卡
Text('精选文创').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
Row() {
Column() {
Text('故宫千里江山系列').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('黄铜书签·丝巾·马克杯').fontSize(11).fontColor('rgba(255,255,255,0.8)').margin({ top: 4 })
Row() {
Text('¥38').fontSize(18).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('起').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ left: 2 })
Text('').layoutWeight(1)
Text('限时分发').fontSize(10).fontColor('#FFFFFF').backgroundColor('rgba(255,255,255,0.25)').padding({ left: 8, right: 8, top: 2, bottom: 2 }).borderRadius(10)
}
.width('100%').margin({ top: 12 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('🏮').fontSize(48)
}
.width('100%').padding(16)
}
.width('100%').margin({ left: 16, right: 16, top: 8 })
.linearGradient({ angle: 135, colors: [['#006064', 0], ['#004D40', 1]] })
.borderRadius(16)
// 双列文创商品
Text('热门文创').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Grid() {
ForEach(CULTURAL_ITEMS_78, (item: CulturalItem78) => {
GridItem() {
Column() {
Column() {
Text('🎨').fontSize(32)
}
.width('100%').height(80)
.backgroundColor(item.color + '20')
.borderRadius({ topLeft: 12, topRight: 12 })
.justifyContent(FlexAlign.Center)
Column() {
Text(item.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(2)
Text(item.origin + ' · ' + item.material).fontSize(9).fontColor('#AAAAAA').margin({ top: 4 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4, top: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
Row() {
Text('★').fontSize(10).fontColor('#FFB300')
Text(item.rating.toString()).fontSize(10).fontColor('#FFB300').margin({ left: 2 })
Text(item.sales + '人付款').fontSize(9).fontColor('#AAAAAA').margin({ left: 8 })
}
.margin({ top: 4 })
Row() {
Text('¥').fontSize(12).fontColor('#FFB300')
Text(item.price.toString()).fontSize(16).fontColor('#FFB300').fontWeight(FontWeight.Bold)
Text('¥' + item.originalPrice).fontSize(10).fontColor('#CCCCCC').decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
Text('').layoutWeight(1)
}
.margin({ top: 4 })
Button() {
Text('购买').fontSize(12).fontColor('#FFFFFF')
}
.width('100%').height(28).margin({ top: 8 })
.backgroundColor(item.color)
.borderRadius(14)
.onClick(() => { this.onBuy(item.id) })
}
.padding(8)
}
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}
}, (item: CulturalItem78) => item.id.toString())
}
.columnsTemplate('1fr 1fr')
.rowsGap(12).columnsGap(12)
.padding(16)
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
// ============ 联名Tab ============
@Component
struct CollabTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 联名IP横滑
Text('联名IP系列').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Scroll() {
Row() {
ForEach(COLLAB_IPS_78, (ip: CollabIP78) => {
Column() {
Column() {
Text('🤝').fontSize(28)
}
.width(140).height(80)
.backgroundColor(ip.color)
.borderRadius({ topLeft: 12, topRight: 12 })
.justifyContent(FlexAlign.Center)
Column() {
Text(ip.name).fontSize(12).fontColor('#333333').fontWeight(FontWeight.Bold).maxLines(1)
Text(ip.brand).fontSize(9).fontColor('#AAAAAA').margin({ top: 2 })
Row() {
Text(ip.status).fontSize(9).fontColor(ip.color).backgroundColor(ip.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
Text(ip.items + '款').fontSize(9).fontColor('#AAAAAA').margin({ left: 4 })
}
.margin({ top: 4 })
Text(ip.date).fontSize(9).fontColor('#AAAAAA').margin({ top: 2 })
}
.padding(8)
}
.width(140).margin({ right: 12 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}, (ip: CollabIP78) => ip.id.toString())
}
.padding({ left: 16, right: 16 })
}
.scrollable(ScrollDirection.Horizontal)
.width('100%').margin({ top: 8 })
// 联名时间轴
Text('联名时间轴').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
ForEach(COLLAB_IPS_78, (ip: CollabIP78, idx: number) => {
Row() {
Column() {
Circle({ width: 12, height: 12 }).fill(ip.color)
if (idx < COLLAB_IPS_78.length - 1) {
Line()
.width(2).layoutWeight(1)
.backgroundColor('#E0E0E0')
.margin({ top: 4 })
}
}
.height('100%')
Column() {
Row() {
Text(ip.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium)
Text(ip.date).fontSize(10).fontColor('#AAAAAA').margin({ left: 8 })
Text('').layoutWeight(1)
Text(ip.status).fontSize(10).fontColor(ip.color)
}
.width('100%')
Text(ip.desc).fontSize(11).fontColor('#999999').margin({ top: 4 })
}
.layoutWeight(1)
.margin({ left: 12, bottom: 16 })
}
.width('100%')
}, (ip: CollabIP78) => ip.id.toString())
}
.width('100%').padding({ left: 16, right: 16 })
// 联名商品列表
Text('联名商品').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 8 })
Column() {
ForEach(COLLAB_ITEMS_78, (item: CollabItem78) => {
Row() {
Column() {
Text('🎨').fontSize(28)
}
.width(80).height(80)
.backgroundColor(item.color + '20')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(1).layoutWeight(1)
if (item.limited) {
Text('限量').fontSize(9).fontColor('#FFFFFF').backgroundColor('#FF5252').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%')
Text(item.ip).fontSize(10).fontColor('#AAAAAA').margin({ top: 2 })
Row() {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
Row() {
Text('★').fontSize(10).fontColor('#FFB300')
Text(item.rating.toString()).fontSize(10).fontColor('#FFB300').margin({ left: 2 })
Text(item.stock > 0 ? '库存' + item.stock : '已售罄').fontSize(9).fontColor(item.stock > 0 ? '#AAAAAA' : '#FF5252').margin({ left: 8 })
Text('').layoutWeight(1)
Text('¥').fontSize(12).fontColor('#FFB300')
Text(item.price.toString()).fontSize(16).fontColor('#FFB300').fontWeight(FontWeight.Bold)
Text('¥' + item.originalPrice).fontSize(10).fontColor('#CCCCCC').decoration({ type: TextDecorationType.LineThrough }).margin({ left: 4 })
}
.width('100%').margin({ top: 4 })
Button() {
Text(item.stock > 0 ? '立即购买' : '到货提醒').fontSize(11).fontColor('#FFFFFF')
}
.height(28).margin({ top: 6 })
.backgroundColor(item.stock > 0 ? item.color : '#BDBDBD')
.borderRadius(14)
.onClick(() => { if (item.stock > 0) { this.onBuy(item.id) } })
}
.margin({ left: 10 })
.layoutWeight(1)
}
.width('100%').padding(10).margin({ top: 8, left: 16, right: 16 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#F0F0F0' })
}, (item: CollabItem78) => item.id.toString())
}
.width('100%')
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.width('100%').height('100%')
.backgroundColor('#F0F7FA')
}
}
// ============ 文具Tab ============
@Component
struct StationeryTab78 {
onBuy: (id: number) => void = () => {}
build() {
Scroll() {
Column() {
// 文具分类网格
Text('文具分类').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 12 })
Grid() {
ForEach(STATIONERY_CATS_78, (cat: StationeryCat78) => {
GridItem() {
Column() {
Text(cat.icon).fontSize(26)
Text(cat.label).fontSize(11).fontColor('#333333').margin({ top: 4 })
Text(cat.count + '件').fontSize(9).fontColor('#AAAAAA')
}
.width('100%').padding({ top: 10, bottom: 10 })
.backgroundColor(cat.bg)
.borderRadius(12)
.alignItems(HorizontalAlign.Center)
}
}, (cat: StationeryCat78) => cat.label)
}
.columnsTemplate('4fr 4fr 4fr 4fr')
.rowsGap(8).columnsGap(8)
.padding(16)
.height(210)
// 限时特价横条
Row() {
Column() {
Text('⚡ 限时特价').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('钢笔/墨水/手账 限时直降').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('距结束 02:18:35').fontSize(10).fontColor('#FFFFFF').backgroundColor('rgba(255,255,255,0.25)').padding({ left: 8, right: 8, top: 2, bottom: 2 }).borderRadius(10)
}
.width('100%').padding(12).margin({ left: 16, right: 16 })
.linearGradient({ angle: 90, colors: [['#FFB300', 0], ['#FF8F00', 1]] })
.borderRadius(12)
// 文具列表
Text('热门文具').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ left: 16, top: 16 })
Column() {
ForEach(STATIONERY_ITEMS_78, (item: StationeryItem78) => {
Row() {
Column() {
Text(item.flashSale ? '⚡' : '✒️').fontSize(28)
}
.width(72).height(72)
.backgroundColor(item.color + '20')
.borderRadius(12)
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(item.name).fontSize(13).fontColor('#333333').fontWeight(FontWeight.Medium).maxLines(1).layoutWeight(1)
if (item.flashSale) {
Text('特价').fontSize(9).fontColor('#FFFFFF').backgroundColor('#FF5252').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4)
}
}
.width('100%')
Text(item.brand).fontSize(10).fontColor('#AAAAAA').margin({ top: 2 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(item.tags, (tag: string) => {
Text(tag).fontSize(9).fontColor(item.color).backgroundColor(item.color + '15').padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).margin({ right: 4, top: 4 })
}, (tag: string) => tag)
}
.margin({ top: 4 })
总结

本文对基于HarmonyOS ArkTS声明式UI范式的多多文创周边应用进行了全面而深入的技术解析。该应用以六大业务Tab为核心,通过国潮、联名、文具、手账、社区与个人中心的模块化设计,构建了一套覆盖文创电商全场景的移动应用平台。应用充分利用了ArkTS的类型系统优势,通过interface定义了十五种数据类型,实现了从商品信息到订单管理、从联名IP到地址管理的完整数据模型。在静态数据初始化方面,常量数组提供了涵盖六大国潮分类、十件文创商品、六个联名IP系列、八件联名商品、八大文具分类、十二件文具商品、六款手账模板等丰富的业务数据,确保了应用在离线状态下的完整功能展示。
从交互设计的角度来看,该应用展现了电商场景下精细化用户体验设计的实践。购买弹窗的规格选择、配送方式、数量调节与动态定价逻辑,通过getTotalPrice方法实现了基础价格加规格加价乘以数量加配送费的完整计算链路。联名时间轴的Circle与Line组件组合,创造了一种独特的信息展示方式。限时特价横条的90度水平渐变与倒计时标签,体现了营销心理学在UI设计中的应用。库存感知的按钮文字与背景色切换,以及限量标签的条件渲染,都展示了数据驱动UI的精确控制能力。会员等级的色彩编码——已达到等级使用鎏金色、未达到使用灰色、当前等级使用浅黄色背景——通过视觉层级清晰地传达了用户的会员进度。
从interface类型定义到常量数据初始化,从主入口组件的Tab路由到各业务Tab的差异化布局,从购买弹窗的动态定价到确认弹窗的统一规范,每一个技术细节都体现了ArkTS声明式UI的开发优势。应用的国潮、联名、文具、手账、社区与个人中心六大模块,虽然各自聚焦不同的业务场景,但在代码结构、视觉风格与交互模式上保持了高度的一致性,这种统一性为应用的后续扩展与维护奠定了坚实的基础。对于希望学习HarmonyOS ArkTS电商应用开发的开发者而言,该应用的组件拆分策略、状态管理模式与数据驱动渲染方式都具有重要的参考价值与实践指导意义。
更多推荐




所有评论(0)