HarmonyOS 6接口驱动开发(Interface-Driven Development)先定义数据接口,再实现业务逻辑和UI组件
在HarmonyOS 6.1.1生态持续繁荣的背景下,ArkTS声明式UI框架为各类垂直领域应用提供了强大的开发能力。本文将深入剖析一款手工皮具定制工坊应用的技术实现,从复古美学设计到完整的电商功能,全方位展示HarmonyOS ArkTS API 24的开发魅力。
马鞍棕与焦糖橙交织的复古色调,手缝工艺与做旧质感的完美融合,这款皮具定制应用将传统工匠精神与现代移动技术相结合,为用户打造沉浸式的手工皮具定制与选购体验。
本文将从接口设计、主题系统、数据建模、动画引擎、组件架构、六大Tab实现等多个维度,系统拆解这款皮具工坊应用的技术内核,为HarmonyOS开发者提供复古风格应用的设计范式与开发参考。
引言

随着HarmonyOS 6.1.1版本的发布,ArkTS声明式UI框架日臻完善,为开发者提供了更加高效、优雅的应用构建体验。这款手工皮具定制工坊应用正是基于HarmonyOS ArkTS API 24打造的一款垂直领域电商应用,它以"手缝工艺·专属定制"为核心理念,将复古美学与现代交互完美融合。
应用整体采用"LT"前缀的命名规范,以马鞍棕(#8B4513)为主色调,焦糖橙(#D2691E)为强调色,搭配做旧米(#F5EDE0)背景与深棕色文字,营造出浓郁的复古手作氛围。从皮革光泽动画到缝线纹理效果,每一个视觉细节都经过精心打磨,充分展现了ArkTS在自定义动画和视觉效果方面的强大能力。
从业务架构来看,这是一款集皮具选购、专属定制、匠人作品、技艺学习、社区交流于一体的综合性手工皮具平台。六大核心Tab页签——首页、钱包、包袋、表带、护理、我的——构建了完整的用户旅程,从浏览选购到定制服务,从护理保养到社区分享,全方位满足皮具爱好者的需求。
技术实现方面,应用采用了模块化的分层架构:底层是TypeScript接口定义与常量数据,中间层是纯函数工具库(包含动画函数、过滤函数、计算函数、查找函数等),上层是ArkTS组件体系(包含主组件、首页组件、各分类Tab组件、个人中心组件)。这种清晰的分层设计不仅保证了代码的可维护性,也充分利用了ArkTS响应式更新的性能优势。
接口定义与数据模型

主题与导航接口
应用的接口定义从主题系统开始,为整个应用的视觉一致性奠定了坚实基础。
interface LtTheme {
bg: string
primary: string
accent: string
card: string
text: string
sub: string
line: string
glow: string
dark: string
}
interface LtTab {
key: string
label: string
icon: Resource
}
interface LtWallet {
name: string
brand: string
leather: string
color: string
size: number
pockets: number
price: number
stock: number
sold: number
tag: string
}
LtTheme接口定义了9个颜色令牌,从背景到文字、从主色到辉光,构成了完整的设计系统。LtTab接口定义了底部导航项的数据结构,包含唯一标识、显示标签和图标资源。LtWallet接口则是钱包产品的数据模型,包含名称、品牌、皮料、颜色、尺寸、卡位、价格、库存、销量和标签等10个属性,全面覆盖了皮具产品的核心特征。
接口驱动开发(Interface-Driven Development)是ArkTS项目的最佳实践之一。先定义数据接口,再实现业务逻辑和UI组件,能够有效减少类型错误,提升代码质量和开发效率。
多品类数据接口

interface LtBag {
name: string
brand: string
leather: string
type: string
capacity: number
weight: number
price: number
stock: number
sold: number
tag: string
}
interface LtStrap {
name: string
brand: string
leather: string
width: number
length: number
buckle: string
price: number
stock: number
sold: number
}
interface LtCare {
name: string
type: string
volume: number
use: string
price: number
stock: number
sold: number
}
除了钱包接口外,应用还定义了包袋(LtBag)、表带(LtStrap)和护理产品(LtCare)三个核心品类接口。每个接口都根据产品特性设计了专属属性:包袋有容量和重量属性,表带有宽度、长度和表扣类型,护理产品有容量和用途。这种领域驱动的接口设计使得数据模型更加精准,也为后续的UI展示和数据处理提供了清晰的语义。
订单、收藏和月度消费等辅助接口则支撑起个人中心的完整功能,形成了从商品展示到交易记录的完整数据闭环。
主题系统与配色设计

复古配色方案
const LT: LtTheme = {
bg: '#F5EDE0',
primary: '#8B4513',
accent: '#D2691E',
card: '#FFFFFF',
text: '#3E2723',
sub: '#8D6E63',
line: '#D7CCC8',
glow: '#BCAAA4',
dark: '#2E1A0E'
}
const LT_TABS: LtTab[] = [
{ key: 'home', label: '首页', icon: $r('app.media.layered_image') },
{ key: 'wallet', label: '钱包', icon: $r('app.media.layered_image') },
{ key: 'bag', label: '包袋', icon: $r('app.media.layered_image') },
{ key: 'strap', label: '表带', icon: $r('app.media.layered_image') },
{ key: 'care', label: '护理', icon: $r('app.media.layered_image') },
{ key: 'mine', label: '我的', icon: $r('app.media.layered_image') }
]
主题常量LT定义了完整的复古配色体系。背景色采用做旧米(#F5EDE0),温暖而不刺眼;主色马鞍棕(#8B4513)沉稳大气,象征着优质皮革的原色;强调色焦糖橙(#D2691E)活泼温暖,用于突出重要信息和操作按钮;辉光色(#BCAAA4)则为皮革质感增添了层次感。
配色方案的选择直接决定了应用的气质。马鞍棕与焦糖橙的组合不仅符合皮具产品的材质特征,更传递出一种经典、耐用、值得信赖的品牌感觉,这种视觉心理学的运用是产品设计成功的关键因素之一。
Tab配置包含六个导航项,从首页到个人中心,覆盖了完整的用户使用路径。每个Tab都有明确的功能定位,信息架构清晰合理。
数据层设计与业务数据

产品数据建模
const LT_WALLETS: LtWallet[] = [
{ name: '马鞍长款', brand: '手工坊', leather: '马鞍革', color: '深棕', size: 19, pockets: 12, price: 2680, stock: 8, sold: 156, tag: '经典' },
{ name: '短款两折', brand: '手工坊', leather: '植鞣革', color: '焦糖', size: 11, pockets: 6, price: 1280, stock: 12, sold: 234, tag: '入门' },
{ name: '拉链短款', brand: '皮匠铺', leather: '铬鞣革', color: '黑色', size: 12, pockets: 8, price: 1680, stock: 10, sold: 198, tag: '实用' },
{ name: '三折大容量', brand: '手工坊', leather: '马鞍革', color: '咖啡', size: 14, pockets: 14, price: 3280, stock: 6, sold: 128, tag: '高端' },
{ name: '极简卡包', brand: '皮匠铺', leather: '植鞣革', color: '原色', size: 10, pockets: 5, price: 880, stock: 18, sold: 312, tag: '入门' },
// ... 更多产品数据
]
钱包数据包含10条记录,涵盖了从入门到高端的完整价格带。皮料类型丰富(马鞍革、植鞣革、铬鞣革、油蜡皮、编织革),标签体系完整(经典、入门、实用、高端、复古、工艺),为筛选功能提供了数据基础。价格从680元到3680元,覆盖了不同消费层级的用户需求。
包袋数据有8条记录,包含手提、斜挎、双肩、手拿、腰包等多种类型;表带数据8条,展示不同宽度、长度和表扣类型的产品;护理产品6条,涵盖保养、护理、防护、清洁、上色、封蜡等全品类。
高质量的模拟数据是前端开发效率的重要保障。在后端接口尚未就绪时,结构完整、数据合理的Mock数据能够让前端开发独立推进,同时也为UI设计提供了真实的展示环境。
动画函数库解析

皮革光泽与缝线动画
function ltShine(wave: number, i: number): number {
let phase: number = (wave + i * 40) % 100
return 0.3 + phase / 100 * 0.7
}
function ltStitch(wave: number, i: number): number {
let phase: number = (wave + i * 50) % 100
return Math.min(phase / 100 * 100, 100)
}
function ltVintage(wave: number, i: number): number {
let v: number = (wave + i * 30) % 200
return v < 100 ? 1.0 : 0.5
}
function ltBuckleRotate(wave: number): number {
return Math.sin(wave / 30) * 45
}
function ltFloatY(wave: number, i: number): number {
return Math.sin(wave / 40 + i) * 6
}
function ltBlink(wave: number, i: number): number {
let v: number = (wave + i * 50) % 200
return v < 100 ? 1.0 : 0.4
}
这款皮具应用拥有6个精心设计的动画函数,是四款应用中动画数量较多的之一。ltShine函数模拟皮革表面的光泽流动效果,透明度在0.3到1.0之间平滑变化;ltStitch函数模拟缝线的渐进效果,呈现出手工缝制的视觉韵律;ltVintage函数则产生复古闪烁效果,模拟旧物的质感。
ltBuckleRotate是一个特色动画函数,它利用正弦函数使表扣产生±45度的旋转摆动,为表带页面增添了生动的交互感。ltFloatY函数实现上下浮动效果,ltBlink函数则产生闪烁呼吸效果。
动画设计应当服务于产品主题,而不是为了动画而动画。皮革光泽、缝线韵律、表扣摆动这些动画效果都与皮具产品的特性紧密相关,能够有效增强用户对产品质感的感知,提升品牌形象。
业务工具函数

function ltFilterWallets(tag: string): LtWallet[] {
let r: LtWallet[] = []
for (let i = 0; i < LT_WALLETS.length; i++) {
if (tag === '全部' || LT_WALLETS[i].tag === tag) {
r.push(LT_WALLETS[i])
}
}
return r
}
function ltSoldRank(): LtWallet[] {
let r: LtWallet[] = LT_WALLETS.slice()
r.sort((a: LtWallet, b: LtWallet) => b.sold - a.sold)
return r.slice(0, 5)
}
function ltPocketW(pockets: number): number {
return Math.min(pockets / 16 * 100, 100)
}
function ltStatusColor(status: string): string {
if (status === '已签收') {
return LT.primary
}
if (status === '定制中') {
return LT.accent
}
if (status === '已退换') {
return '#999999'
}
return LT.sub
}
业务工具函数层承担了数据处理的核心职责。筛选函数ltFilterWallets按标签过滤钱包列表,排序函数ltSoldRank按销量降序返回前5名,计算函数ltPocketW将卡位数转换为百分比宽度用于可视化。
值得关注的是ltStatusColor函数中的订单状态设计:“已签收”、“定制中”、"已退换"三种状态分别对应主色、强调色和灰色。其中"定制中"是这款皮具应用特有的状态,体现了定制业务的核心特征——用户下单后产品进入手工制作阶段,需要一定的制作周期。
订单状态设计是电商应用的核心业务逻辑之一。不同行业的订单状态差异很大,标准化的状态体系能够提升用户体验,减少客服沟通成本。
主组件架构与首页实现

主组件状态管理
@Entry
@Component
struct LtApp {
@State tab: string = 'home'
@State showHomeCustom: boolean = false
@State showHomeCourse: boolean = false
@State showHomeCraftsman: boolean = false
@State showHomeCommunity: boolean = false
@Builder modalOverlay() {
Column() {
Column().width('100%').height('100%').backgroundColor('rgba(46,26,14,0.62)')
}
.width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
.onClick(() => {
this.showHomeCustom = false
this.showHomeCourse = false
this.showHomeCraftsman = false
this.showHomeCommunity = false
})
}
主组件LtApp管理5个状态变量:1个Tab切换状态和4个弹框显示状态。弹框功能包括皮具定制、皮具课堂、匠人作品和皮具社区,这四个功能对应首页的四个快捷入口。遮罩层使用深棕色调的半透明背景(rgba(46,26,14,0.62)),与整体复古色调保持一致。
遮罩层的颜色选择是一个容易被忽略但非常重要的细节。使用与主色调一致的半透明色,而不是通用的黑色半透明,能够让弹框出现时整体画面更加和谐,进一步强化品牌视觉识别。
皮革光泽Banner
// 皮革光泽 banner
Stack() {
Column().width('100%').height(120)
.linearGradient({ angle: 135, colors: [[LT.primary, 0], [LT.dark, 1]] })
Column() {
Text('手缝工艺').fontSize(28).fontWeight(900).fontColor('#F5EDE0').letterSpacing(3)
Text('手工皮具 · 专属定制').fontSize(12).fontColor('rgba(245,237,224,0.8)').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center).margin({ top: 20 })
// 光泽流动效果
Row() {
ForEach([0, 1, 2, 3], (i: number) => {
Column().width(40).height(40).borderRadius(20).backgroundColor('rgba(245,237,224,0.15)')
.opacity(ltShine(this.wave, i))
.margin({ left: 8, right: 8 })
}, (i: number) => i.toString())
}
.margin({ top: 60 })
}
.width('100%').height(120)
首页Banner采用皮革光泽效果设计,底层是马鞍棕到深棕色的135度渐变,模拟优质皮革的色彩过渡。标题"手缝工艺"采用大号粗体字,配合字间距调整,传递出工匠精神。光泽流动效果通过四个圆形光斑的透明度变化实现,ltShine函数为每个光斑计算不同的相位,形成流动的光泽感。
做旧纹理仪表盘
// 做旧纹理仪表盘
Row() {
Stack() {
Column().width(100).height(100).borderRadius(50).border({ width: 4, color: LT.line })
Column().width(80).height(80).borderRadius(40).backgroundColor(LT.card)
Column() {
Text('卡位').fontSize(10).fontColor(LT.sub)
Text('16').fontSize(24).fontWeight(900).fontColor(LT.primary).margin({ top: 2 })
Text('个').fontSize(10).fontColor(LT.sub)
}
.alignItems(HorizontalAlign.Center)
}
.width(110).height(110)
Column() {
Text('镇店之宝').fontSize(11).fontColor(LT.sub)
Text('商务长款').fontSize(20).fontWeight(800).fontColor(LT.dark).margin({ top: 4 })
Text('马鞍革 | 手缝 | 16卡位').fontSize(11).fontColor(LT.sub).margin({ top: 4 })
Text('定制价 ¥3,680').fontSize(11).fontColor(LT.accent).fontWeight(600).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1).padding({ left: 12 })
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
镇店之宝仪表盘展示了一款商务长款钱包的核心信息。左侧同心圆设计中,中心显示"16个卡位"的核心卖点,右侧是产品名称、材质、工艺和价格等详细信息。"手缝"二字突出了手工工艺的价值,"定制价"则暗示了这款产品支持个性化定制服务。
应用架构流程图
钱包Tab与定制功能
筛选与双列网格布局
// 筛选 chips
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '经典', '入门', '高端', '复古', '工艺'], (t: string) => {
Text(t).fontSize(12).fontColor(this.filterTag === t ? LT.card : LT.text)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.filterTag === t ? LT.primary : LT.bg)
.onClick(() => {
this.filterTag = t
this.list = ltFilterWallets(t)
})
}, (t: string) => t)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
.width('100%')
.height(36)
钱包Tab的筛选Chips提供了"全部、经典、入门、高端、复古、工艺"六个标签选项,覆盖了皮具产品的主要分类维度。选中状态通过马鞍棕背景+米白文字的组合来标识,未选中状态则是米色背景+深棕文字,整体色调统一和谐。
筛选标签的设计需要平衡两个目标:一是覆盖用户可能的筛选维度,二是避免标签过多造成选择困难。六个标签的数量处于合理范围,既提供了足够的筛选粒度,又不会让用户感到困惑。
双列网格产品卡片
// 双列网格
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.list, (w: LtWallet, i: number) => {
Column() {
Stack() {
Column().width('100%').height(70).borderRadius(12)
.linearGradient({ angle: 135, colors: [[LT.glow, 0], [LT.primary, 1]] })
Column().width(28).height(28).borderRadius(14).backgroundColor('rgba(245,237,224,0.3)')
.opacity(ltShine(this.wave, i))
}
.width('100%').height(70)
Text(w.name).fontSize(13).fontWeight(700).fontColor(LT.text).margin({ top: 8 })
Text(w.leather + ' · ' + w.color).fontSize(10).fontColor(LT.sub).margin({ top: 2 })
Row() {
Text(w.tag).fontSize(10).fontColor(LT.accent).fontWeight(600)
Column().layoutWeight(1)
Text(w.pockets + '卡位').fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
Row() {
Text('¥').fontSize(10).fontColor(LT.sub)
Text(w.price.toString()).fontSize(14).fontColor(LT.accent).fontWeight(700)
Column().layoutWeight(1)
Text('库存 ' + w.stock.toString()).fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
}
.width('48.5%')
.padding(10)
.backgroundColor(LT.card)
.borderRadius(12)
.margin({ bottom: 8 })
.onClick(() => {
this.selName = w.name
this.showDetail = true
})
}, (w: LtWallet) => w.name)
}
.width('100%').padding({ left: 12, right: 12 })
产品卡片采用双列网格布局,使用Flex组件的wrap: FlexWrap.Wrap属性实现自动换行。每张卡片包含渐变预览图、产品名称、皮料颜色、标签卡位、价格库存等信息,信息密度适中,层次分明。
卡片顶部的渐变区域模拟皮革产品的视觉效果,中心的圆形光斑通过ltShine函数驱动透明度变化,形成皮革光泽流动的视觉效果。这种微动画设计虽然 subtle,但能有效提升界面的精致感和品质感。
产品卡片是电商应用中最常见的UI组件之一,其设计质量直接影响用户的购买决策。信息层级的合理安排、价格的突出展示、库存的适度提示,都是经过深思熟虑的设计决策。
定制功能弹框深度解析
if (this.showHomeCustom) {
Stack() {
this.modalOverlay()
Column() {
Text('皮具定制').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('专属定制您的皮具作品').fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('皮料:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('马鞍革').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('植鞣革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('油蜡皮').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('编织革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 16 })
Column() {
Text('颜色:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('深棕').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.accent)
Text('焦糖').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('黑棕').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('酒红').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 12 })
Column() {
Text('缝线工艺:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('手缝').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('机缝').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 12 })
Row() {
Text('定制费:').fontSize(13).fontColor(LT.text).fontWeight(600)
Text('¥800起').fontSize(18).fontWeight(800).fontColor(LT.accent)
Column().layoutWeight(1)
Text('立即定制').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
皮具定制弹框是应用的核心功能之一,体现了手工皮具的业务特色。弹框包含三个核心定制维度:皮料选择(马鞍革、植鞣革、油蜡皮、编织革)、颜色选择(深棕、焦糖、黑棕、酒红)和缝线工艺(手缝、机缝)。每个维度都提供多个选项,用户可以根据自己的喜好进行组合定制。
定制费采用"¥800起"的定价方式,既给出了价格预期,又为复杂定制留出了议价空间。"立即定制"按钮使用主色调,引导用户进行下一步操作。
定制功能的UI设计需要在灵活性和易用性之间找到平衡。提供太多选项会让用户不知所措,选项太少又无法满足个性化需求。三个维度、每维度3-4个选项是一个比较合理的配置,既能体现定制感,又不会过度增加决策负担。
"我的"Tab与会员体系
会员卡与消费统计
// 会员卡
Stack() {
Column().width('100%').height(120).borderRadius(16)
.linearGradient({ angle: 135, colors: [[LT.dark, 0], [LT.primary, 1]] })
Column() {
Text('LEATHER VIP').fontSize(22).fontWeight(900).fontColor('#F5EDE0').letterSpacing(2)
Text('手工皮具会员').fontSize(12).fontColor('rgba(245,237,224,0.7)').margin({ top: 4 })
Row() {
Text('积分').fontSize(11).fontColor('rgba(245,237,224,0.7)')
Text('8,680').fontSize(20).fontWeight(800).fontColor('#F5EDE0').margin({ left: 8 })
}
.margin({ top: 12 })
}
.alignItems(HorizontalAlign.Center).margin({ top: 24 })
Column().width(60).height(60).borderRadius(30).backgroundColor('rgba(245,237,224,0.15)')
.opacity(ltVintage(this.wave, 0))
.position({ x: 280, y: 16 })
}
.width('100%').height(120).padding({ left: 16, right: 16, top: 16 })
会员卡设计延续了整体的复古风格,采用深棕到马鞍棕的渐变背景,配合"LEATHER VIP"的英文字样,传递出高端会员的尊贵感。积分数字"8,680"使用大号白色字体,清晰醒目。右上角的圆形光晕使用ltVintage函数驱动,产生复古闪烁的效果。
会员体系是电商应用提高用户留存的重要手段。通过积分、等级、专属权益等设计,可以有效提升用户的粘性和复购率。在UI层面,会员卡的视觉设计直接影响用户对会员价值的感知。
订单与个人设置
// 订单列表
Column() {
Text('我的订单').fontSize(13).fontWeight(700).fontColor(LT.dark).margin({ bottom: 8 })
ForEach(this.orders, (o: LtOrder, i: number) => {
Row() {
Column() {
Text(o.item).fontSize(13).fontWeight(600).fontColor(LT.text)
Text(o.id + ' · ' + o.date).fontSize(10).fontColor(LT.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column() {
Text('¥' + o.amount.toString()).fontSize(13).fontWeight(700).fontColor(LT.text)
Text(o.status).fontSize(10).fontColor(ltStatusColor(o.status)).margin({ top: 2 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%').padding({ top: 8, bottom: 8 })
.border({ width: { bottom: 1 }, color: LT.line })
}, (o: LtOrder) => o.id)
}
.width('94%').padding(16).backgroundColor(LT.card).borderRadius(12).margin({ top: 8 })
订单列表采用经典的左右布局,左侧显示商品名称和订单号,右侧显示金额和状态。状态颜色通过ltStatusColor函数动态计算,"定制中"状态使用焦糖橙色,既醒目又与整体色调协调。
设置列表包含昵称、收货地址、关于我们三个选项,采用标准的列表项样式,点击后分别弹出对应的设置弹框。
技术点对比表格
| 技术维度 | 皮具工坊 (LT) | 盆景艺术 (BN) | 纹身潮流 (IW) | 玻璃艺术 (VG) |
|---|---|---|---|---|
| 设计风格 | 复古手作风 | 新中式禅意 | 潮流纹身风 | 彩绘玻璃风 |
| 背景色系 | 做旧米 #F5EDE0 | 米白 #F5F2E8 | 深紫黑 #0F0A1A | 淡紫 #F1EFF7 |
| 主色调 | 马鞍棕 #8B4513 | 苔藓绿 #5B7C5A | 荧光青 #00FFFF | 靛蓝 #4338CA |
| 强调色 | 焦糖橙 #D2691E | 陶土棕 #B5651D | 电粉 #FF1493 | 琥珀金 #D97706 |
| 动画数量 | 6个 | 4个 | 6个 | 4个 |
| 特色动画 | 皮革光泽/缝线/表扣旋转 | 水墨晕染/摇摆 | 墨水滴落/旋转辉光 | 光线折射/玻璃光泽 |
| 商品品类 | 钱包/包袋/表带/护理 | 松柏/杂木/山水/盆器 | 纹身贴/工具/护理/图案 | 玻璃/作品/工具/课程 |
| 首页功能 | 定制/课堂/匠人/社区 | 展览/课堂/拍卖/社区 | 定制/课堂/店铺/社区 | (多样化入口) |
| 订单状态 | 已签收/定制中/已退换 | 已签收/养护中/已退换 | 已签收/运输中/已退换 | 已完成/制作中/待开课 |
| 特色亮点 | 手工定制+匠人体系 | 东方禅意+艺术展览 | 深色主题+荧光效果 | 彩色玻璃+光效 |
安装DevEco Studio程序

选择目标安装目录:

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

新建一个空白模板:

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

完整代码:
// LT 前缀 | 马鞍棕×焦糖橙×做旧米 | 复古手作风 | 浅色暖调 | 6 tab
// ============ 接口定义 ============
interface LtTheme {
bg: string
primary: string
accent: string
card: string
text: string
sub: string
line: string
glow: string
dark: string
}
interface LtTab {
key: string
label: string
icon: Resource
}
interface LtWallet {
name: string
brand: string
leather: string
color: string
size: number
pockets: number
price: number
stock: number
sold: number
tag: string
}
interface LtBag {
name: string
brand: string
leather: string
type: string
capacity: number
weight: number
price: number
stock: number
sold: number
tag: string
}
interface LtStrap {
name: string
brand: string
leather: string
width: number
length: number
buckle: string
price: number
stock: number
sold: number
}
interface LtCare {
name: string
type: string
volume: number
use: string
price: number
stock: number
sold: number
}
interface LtOrder {
id: string
item: string
date: string
status: string
amount: number
}
interface LtCollect {
name: string
brand: string
price: number
tag: string
}
interface LtMonth {
month: string
amount: number
}
// ============ 主题常量 ============
const LT: LtTheme = {
bg: '#F5EDE0',
primary: '#8B4513',
accent: '#D2691E',
card: '#FFFFFF',
text: '#3E2723',
sub: '#8D6E63',
line: '#D7CCC8',
glow: '#BCAAA4',
dark: '#2E1A0E'
}
const LT_TABS: LtTab[] = [
{ key: 'home', label: '首页', icon: $r('app.media.layered_image') },
{ key: 'wallet', label: '钱包', icon: $r('app.media.layered_image') },
{ key: 'bag', label: '包袋', icon: $r('app.media.layered_image') },
{ key: 'strap', label: '表带', icon: $r('app.media.layered_image') },
{ key: 'care', label: '护理', icon: $r('app.media.layered_image') },
{ key: 'mine', label: '我的', icon: $r('app.media.layered_image') }
]
// ============ 数据:钱包 10 条 ============
const LT_WALLETS: LtWallet[] = [
{ name: '马鞍长款', brand: '手工坊', leather: '马鞍革', color: '深棕', size: 19, pockets: 12, price: 2680, stock: 8, sold: 156, tag: '经典' },
{ name: '短款两折', brand: '手工坊', leather: '植鞣革', color: '焦糖', size: 11, pockets: 6, price: 1280, stock: 12, sold: 234, tag: '入门' },
{ name: '拉链短款', brand: '皮匠铺', leather: '铬鞣革', color: '黑色', size: 12, pockets: 8, price: 1680, stock: 10, sold: 198, tag: '实用' },
{ name: '三折大容量', brand: '手工坊', leather: '马鞍革', color: '咖啡', size: 14, pockets: 14, price: 3280, stock: 6, sold: 128, tag: '高端' },
{ name: '极简卡包', brand: '皮匠铺', leather: '植鞣革', color: '原色', size: 10, pockets: 5, price: 880, stock: 18, sold: 312, tag: '入门' },
{ name: '商务长款', brand: '手工坊', leather: '马鞍革', color: '黑棕', size: 20, pockets: 16, price: 3680, stock: 5, sold: 89, tag: '高端' },
{ name: '复古双折', brand: '皮匠铺', leather: '油蜡皮', color: '复古棕', size: 12, pockets: 7, price: 1480, stock: 14, sold: 167, tag: '复古' },
{ name: '编织短款', brand: '手工坊', leather: '编织革', color: '蜜色', size: 11, pockets: 6, price: 1880, stock: 9, sold: 145, tag: '工艺' },
{ name: '手抓长款', brand: '皮匠铺', leather: '植鞣革', color: '焦糖', size: 18, pockets: 10, price: 2280, stock: 7, sold: 112, tag: '经典' },
{ name: '迷你零钱包', brand: '手工坊', leather: '铬鞣革', color: '酒红', size: 9, pockets: 3, price: 680, stock: 20, sold: 378, tag: '入门' }
]
// ============ 数据:包袋 8 条 ============
const LT_BAGS: LtBag[] = [
{ name: '托特手提', brand: '手工坊', leather: '马鞍革', type: '手提', capacity: 15, weight: 0.85, price: 4680, stock: 6, sold: 128, tag: '经典' },
{ name: '单肩斜挎', brand: '皮匠铺', leather: '植鞣革', type: '斜挎', capacity: 8, weight: 0.52, price: 2680, stock: 10, sold: 198, tag: '日常' },
{ name: '商务公文', brand: '手工坊', leather: '铬鞣革', type: '手提', capacity: 20, weight: 1.2, price: 5880, stock: 4, sold: 78, tag: '高端' },
{ name: '邮差包', brand: '皮匠铺', leather: '油蜡皮', type: '斜挎', capacity: 12, weight: 0.68, price: 3280, stock: 8, sold: 156, tag: '复古' },
{ name: '双肩背包', brand: '手工坊', leather: '马鞍革', type: '双肩', capacity: 25, weight: 1.5, price: 5280, stock: 5, sold: 89, tag: '高端' },
{ name: '手拿信封', brand: '皮匠铺', leather: '植鞣革', type: '手拿', capacity: 5, weight: 0.3, price: 1880, stock: 12, sold: 234, tag: '入门' },
{ name: '腰包小包', brand: '手工坊', leather: '编织革', type: '腰包', capacity: 4, weight: 0.25, price: 1480, stock: 15, sold: 267, tag: '实用' },
{ name: '波士顿包', brand: '皮匠铺', leather: '油蜡皮', type: '手提', capacity: 18, weight: 1.1, price: 4280, stock: 6, sold: 112, tag: '经典' }
]
// ============ 数据:表带 8 条 ============
const LT_STRAPS: LtStrap[] = [
{ name: '经典马鞍带', brand: '手工坊', leather: '马鞍革', width: 22, length: 120, buckle: '针扣', price: 880, stock: 14, sold: 234 },
{ name: '植鞣原色带', brand: '皮匠铺', leather: '植鞣革', width: 20, length: 115, buckle: '针扣', price: 680, stock: 18, sold: 312 },
{ name: '油蜡复古带', brand: '手工坊', leather: '油蜡皮', width: 22, length: 120, buckle: '蝴蝶扣', price: 980, stock: 10, sold: 178 },
{ name: '编织手环带', brand: '皮匠铺', leather: '编织革', width: 18, length: 110, buckle: '针扣', price: 780, stock: 12, sold: 198 },
{ name: '宽版军装带', brand: '手工坊', leather: '马鞍革', width: 26, length: 125, buckle: '针扣', price: 1080, stock: 8, sold: 156 },
{ name: '细款女装带', brand: '皮匠铺', leather: '植鞣革', width: 14, length: 105, buckle: '珠宝扣', price: 880, stock: 15, sold: 224 },
{ name: '做旧咖啡带', brand: '手工坊', leather: '油蜡皮', width: 22, length: 120, buckle: '蝴蝶扣', price: 1180, stock: 7, sold: 134 },
{ name: '极简黑棕带', brand: '皮匠铺', leather: '铬鞣革', width: 20, length: 115, buckle: '针扣', price: 780, stock: 16, sold: 267 }
]
// ============ 数据:护理 6 条 ============
const LT_CARE: LtCare[] = [
{ name: '蜂蜡保养膏', type: '保养', volume: 50, use: '滋润防水', price: 168, stock: 30, sold: 456 },
{ name: '貂油护理液', type: '护理', volume: 100, use: '深层滋养', price: 128, stock: 25, sold: 378 },
{ name: '防水喷雾', type: '防护', volume: 200, use: '防水防污', price: 98, stock: 35, sold: 312 },
{ name: '清洁泡沫', type: '清洁', volume: 150, use: '温和清洁', price: 88, stock: 28, sold: 289 },
{ name: '上色蜡', type: '上色', volume: 30, use: '补色修复', price: 148, stock: 20, sold: 198 },
{ name: '做旧封蜡', type: '封蜡', volume: 40, use: '固定做旧', price: 188, stock: 15, sold: 156 }
]
// ============ 数据:订单 6 条 ============
const LT_ORDERS: LtOrder[] = [
{ id: 'LT20260812', item: '马鞍长款钱包', date: '08-12', status: '已签收', amount: 2680 },
{ id: 'LT20260808', item: '蜂蜡保养膏', date: '08-08', status: '已签收', amount: 168 },
{ id: 'LT20260730', item: '托特手提包', date: '07-30', status: '定制中', amount: 4680 },
{ id: 'LT20260722', item: '经典马鞍表带', date: '07-22', status: '已签收', amount: 880 },
{ id: 'LT20260715', item: '商务公文包', date: '07-15', status: '已签收', amount: 5880 },
{ id: 'LT20260701', item: '极简卡包', date: '07-01', status: '已退换', amount: 880 }
]
// ============ 数据:收藏 6 条 ============
const LT_COLLECTS: LtCollect[] = [
{ name: '商务长款钱包', brand: '手工坊', price: 3680, tag: '高端' },
{ name: '双肩背包', brand: '手工坊', price: 5280, tag: '高端' },
{ name: '做旧咖啡表带', brand: '手工坊', price: 1180, tag: '复古' },
{ name: '商务公文包', brand: '手工坊', price: 5880, tag: '高端' },
{ name: '三折大容量钱包', brand: '手工坊', price: 3280, tag: '高端' },
{ name: '波士顿包', brand: '皮匠铺', price: 4280, tag: '经典' }
]
// ============ 数据:月度消费 6 条 ============
const LT_MONTHS: LtMonth[] = [
{ month: '03月', amount: 2680 },
{ month: '04月', amount: 1480 },
{ month: '05月', amount: 4680 },
{ month: '06月', amount: 3280 },
{ month: '07月', amount: 6440 },
{ month: '08月', amount: 2848 }
]
// ============ 全局纯函数 ============
function ltShine(wave: number, i: number): number {
let phase: number = (wave + i * 40) % 100
return 0.3 + phase / 100 * 0.7
}
function ltStitch(wave: number, i: number): number {
let phase: number = (wave + i * 50) % 100
return Math.min(phase / 100 * 100, 100)
}
function ltVintage(wave: number, i: number): number {
let v: number = (wave + i * 30) % 200
return v < 100 ? 1.0 : 0.5
}
function ltBuckleRotate(wave: number): number {
return Math.sin(wave / 30) * 45
}
function ltFloatY(wave: number, i: number): number {
return Math.sin(wave / 40 + i) * 6
}
function ltBlink(wave: number, i: number): number {
let v: number = (wave + i * 50) % 200
return v < 100 ? 1.0 : 0.4
}
function ltFilterWallets(tag: string): LtWallet[] {
let r: LtWallet[] = []
for (let i = 0; i < LT_WALLETS.length; i++) {
if (tag === '全部' || LT_WALLETS[i].tag === tag) {
r.push(LT_WALLETS[i])
}
}
return r
}
function ltFilterBags(tag: string): LtBag[] {
let r: LtBag[] = []
for (let i = 0; i < LT_BAGS.length; i++) {
if (tag === '全部' || LT_BAGS[i].tag === tag) {
r.push(LT_BAGS[i])
}
}
return r
}
function ltSoldRank(): LtWallet[] {
let r: LtWallet[] = LT_WALLETS.slice()
r.sort((a: LtWallet, b: LtWallet) => b.sold - a.sold)
return r.slice(0, 5)
}
function ltPocketW(pockets: number): number {
return Math.min(pockets / 16 * 100, 100)
}
function ltWidthW(width: number): number {
return Math.min(width / 26 * 100, 100)
}
function ltLengthW(length: number): number {
return Math.min(length / 125 * 100, 100)
}
function ltPriceW(p: number): number {
return Math.min(p / 5880 * 100, 100)
}
function ltCapacityW(c: number): number {
return Math.min(c / 25 * 100, 100)
}
function ltVolumeW(v: number): number {
return Math.min(v / 200 * 100, 100)
}
function ltMonthH(amount: number): number {
let max: number = 6440
return Math.max(amount / max * 120, 8)
}
function ltTopN(i: number): string {
return ['1', '2', '3', '4', '5'][i] || ''
}
function ltStatusColor(status: string): string {
if (status === '已签收') {
return LT.primary
}
if (status === '定制中') {
return LT.accent
}
if (status === '已退换') {
return '#999999'
}
return LT.sub
}
function ltWalletByName(name: string): LtWallet {
let idx: number = 0
for (let i = 0; i < LT_WALLETS.length; i++) {
if (LT_WALLETS[i].name === name) {
idx = i
}
}
return LT_WALLETS[idx]
}
function ltBagByName(name: string): LtBag {
let idx: number = 0
for (let i = 0; i < LT_BAGS.length; i++) {
if (LT_BAGS[i].name === name) {
idx = i
}
}
return LT_BAGS[idx]
}
function ltStrapByName(name: string): LtStrap {
let idx: number = 0
for (let i = 0; i < LT_STRAPS.length; i++) {
if (LT_STRAPS[i].name === name) {
idx = i
}
}
return LT_STRAPS[idx]
}
function ltCareByName(name: string): LtCare {
let idx: number = 0
for (let i = 0; i < LT_CARE.length; i++) {
if (LT_CARE[i].name === name) {
idx = i
}
}
return LT_CARE[idx]
}
// ============ 主组件 ============
@Entry
@Component
struct LtApp {
@State tab: string = 'home'
@State showHomeCustom: boolean = false
@State showHomeCourse: boolean = false
@State showHomeCraftsman: boolean = false
@State showHomeCommunity: boolean = false
@Builder modalOverlay() {
Column() {
Column().width('100%').height('100%').backgroundColor('rgba(46,26,14,0.62)')
}
.width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
.onClick(() => {
this.showHomeCustom = false
this.showHomeCourse = false
this.showHomeCraftsman = false
this.showHomeCommunity = false
})
}
build() {
Stack() {
Column() {
// 头部品牌栏
Row() {
Column() {
Text('皮具').fontSize(22).fontWeight(900).fontColor(LT.dark)
Text('LEATHER WORKS').fontSize(10).fontColor(LT.sub).letterSpacing(2)
}
.alignItems(HorizontalAlign.Start)
Column().layoutWeight(1)
Row() {
Text('手缝工艺').fontSize(12).fontColor(LT.sub)
Column().width(2).height(12).backgroundColor(LT.line).margin({ left: 8, right: 8 })
Text('马鞍革').fontSize(10).fontColor(LT.accent).fontWeight(700)
}
}
.width('100%').height(56).padding({ left: 16, right: 16 }).backgroundColor(LT.card)
// 搜索条
Row() {
Row() {
Text('🔍').fontSize(14).fontColor(LT.sub).margin({ left: 12, right: 8 })
Text('搜索钱包 / 包袋 / 表带...').fontSize(13).fontColor(LT.sub)
Column().layoutWeight(1)
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.primary).onClick(() => {})
}
.width('100%').height(40).backgroundColor(LT.bg).borderRadius(20).padding({ left: 4, right: 4 })
}
.width('100%').padding({ left: 12, right: 12, top: 8, bottom: 8 })
// tab 内容区
Column() {
if (this.tab === 'home') {
LtHomeTab({
onCustom: () => { this.showHomeCustom = true },
onCourse: () => { this.showHomeCourse = true },
onCraftsman: () => { this.showHomeCraftsman = true },
onCommunity: () => { this.showHomeCommunity = true }
})
} else if (this.tab === 'wallet') {
LtWalletTab()
} else if (this.tab === 'bag') {
LtBagTab()
} else if (this.tab === 'strap') {
LtStrapTab()
} else if (this.tab === 'care') {
LtCareTab()
} else {
LtMineTab()
}
}
.layoutWeight(1)
// 底部 tab 栏
Row() {
ForEach(LT_TABS, (t: LtTab) => {
Column() {
Text(t.icon).width(20).height(20).fontColor(this.tab === t.key ? LT.primary : LT.sub)
Text(t.label).fontSize(10).fontColor(this.tab === t.key ? LT.primary : LT.sub).fontWeight(this.tab === t.key ? 700 : 400)
}
.layoutWeight(1)
.padding({ top: 6, bottom: 6 })
.onClick(() => {
this.tab = t.key
})
}, (t: LtTab) => t.key)
}
.width('100%').height(52).backgroundColor(LT.card)
.border({ width: { top: 1 }, color: LT.line })
}
.width('100%').height('100%').backgroundColor(LT.bg)
// 首页弹框
if (this.showHomeCustom) {
Stack() {
this.modalOverlay()
Column() {
Text('皮具定制').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('专属定制您的皮具作品').fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('皮料:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('马鞍革').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('植鞣革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('油蜡皮').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('编织革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 16 })
Column() {
Text('颜色:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('深棕').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.accent)
Text('焦糖').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('黑棕').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('酒红').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 12 })
Column() {
Text('缝线工艺:').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 8 })
Row() {
Text('手缝').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('机缝').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
.width('100%')
}
.width('100%').margin({ top: 12 })
Row() {
Text('定制费:').fontSize(13).fontColor(LT.text).fontWeight(600)
Text('¥800起').fontSize(18).fontWeight(800).fontColor(LT.accent)
Column().layoutWeight(1)
Text('立即定制').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
if (this.showHomeCourse) {
Stack() {
this.modalOverlay()
Column() {
Text('皮具课堂').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('学习手工皮具制作技艺').fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('课程一:基础裁切与缝线').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 4 })
Text('讲师:陈师傅 | 3小时 | ¥580').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 16 })
Column() {
Text('课程二:皮料选择与鉴别').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 4 })
Text('讲师:周师傅 | 2小时 | ¥480').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 8 })
Column() {
Text('课程三:做旧与封蜡技巧').fontSize(13).fontColor(LT.text).fontWeight(600).margin({ bottom: 4 })
Text('讲师:王师傅 | 1.5小时 | ¥380').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 8 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showHomeCourse = false })
Column().layoutWeight(1)
Text('预约全部').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
if (this.showHomeCraftsman) {
Stack() {
this.modalOverlay()
Column() {
Text('匠人作品').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('精选匠人定制系列').fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Row() {
ForEach(['#8B4513', '#D2691E', '#BCAAA4', '#2E1A0E'], (c: string) => {
Column().width(36).height(36).borderRadius(18).backgroundColor(c).margin({ left: 6, right: 6 })
}, (c: string, i: number) => `${c}_${i}`)
}
.margin({ top: 16 })
Column() {
Text('当前作品:商务长款钱包').fontSize(14).fontColor(LT.text).fontWeight(600).margin({ bottom: 4 })
Text('马鞍革 | 手缝 | 16卡位').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 16 })
Row() {
Text('定制价:').fontSize(13).fontColor(LT.text).fontWeight(600)
Text('¥3,680').fontSize(20).fontWeight(800).fontColor(LT.accent)
Column().layoutWeight(1)
Text('预约定制').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.accent)
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
if (this.showHomeCommunity) {
Stack() {
this.modalOverlay()
Column() {
Text('皮具社区').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('与皮具爱好者交流分享').fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('"养了三个月的马鞍革长款,包浆越来越漂亮"').fontSize(13).fontColor(LT.text).margin({ bottom: 4 })
Text('@皮革控 | 2小时前').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 16 })
Column() {
Text('"第一次尝试手缝,针脚不太均匀但很有成就感"').fontSize(13).fontColor(LT.text).margin({ bottom: 4 })
Text('@手作新人 | 5小时前').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 8 })
Column() {
Text('"油蜡皮做旧效果绝了,复古感拉满"').fontSize(13).fontColor(LT.text).margin({ bottom: 4 })
Text('@老皮匠 | 1天前').fontSize(11).fontColor(LT.sub)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 8 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showHomeCommunity = false })
Column().layoutWeight(1)
Text('加入社区').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
}
.width('100%').height('100%')
}
}
// ============ Tab 1: 首页 ============
@Component
struct LtHomeTab {
@State wave: number = 0
@State walletList: LtWallet[] = []
@State rankList: LtWallet[] = []
private timer: number = -1
@State showDetail: boolean = false
@State selName: string = ''
onCustom: () => void = () => {}
onCourse: () => void = () => {}
onCraftsman: () => void = () => {}
onCommunity: () => void = () => {}
aboutToAppear() {
this.walletList = LT_WALLETS.slice(0, 4)
this.rankList = ltSoldRank()
this.timer = setInterval(() => {
this.wave += 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder modalOverlay() {
Column() {
Column().width('100%').height('100%').backgroundColor('rgba(46,26,14,0.62)')
}
.width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
.onClick(() => {
this.showDetail = false
})
}
build() {
Stack() {
Scroll() {
Column() {
// 皮革光泽 banner
Stack() {
Column().width('100%').height(120)
.linearGradient({ angle: 135, colors: [[LT.primary, 0], [LT.dark, 1]] })
Column() {
Text('手缝工艺').fontSize(28).fontWeight(900).fontColor('#F5EDE0').letterSpacing(3)
Text('手工皮具 · 专属定制').fontSize(12).fontColor('rgba(245,237,224,0.8)').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center).margin({ top: 20 })
// 光泽流动效果
Row() {
ForEach([0, 1, 2, 3], (i: number) => {
Column().width(40).height(40).borderRadius(20).backgroundColor('rgba(245,237,224,0.15)')
.opacity(ltShine(this.wave, i))
.margin({ left: 8, right: 8 })
}, (i: number) => i.toString())
}
.margin({ top: 60 })
}
.width('100%').height(120)
// 做旧纹理仪表盘
Row() {
Stack() {
Column().width(100).height(100).borderRadius(50).border({ width: 4, color: LT.line })
Column().width(80).height(80).borderRadius(40).backgroundColor(LT.card)
Column() {
Text('卡位').fontSize(10).fontColor(LT.sub)
Text('16').fontSize(24).fontWeight(900).fontColor(LT.primary).margin({ top: 2 })
Text('个').fontSize(10).fontColor(LT.sub)
}
.alignItems(HorizontalAlign.Center)
}
.width(110).height(110)
Column() {
Text('镇店之宝').fontSize(11).fontColor(LT.sub)
Text('商务长款').fontSize(20).fontWeight(800).fontColor(LT.dark).margin({ top: 4 })
Text('马鞍革 | 手缝 | 16卡位').fontSize(11).fontColor(LT.sub).margin({ top: 4 })
Text('定制价 ¥3,680').fontSize(11).fontColor(LT.accent).fontWeight(600).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1).padding({ left: 12 })
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
// 4 格统计卡
Row() {
Column() {
Text('82').fontSize(22).fontWeight(800).fontColor(LT.primary)
Text('钱包').fontSize(11).fontColor(LT.sub).margin({ top: 2 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 12, bottom: 12 })
Column().width(1).height(36).backgroundColor(LT.line)
Column() {
Text('48').fontSize(22).fontWeight(800).fontColor(LT.accent)
Text('包袋').fontSize(11).fontColor(LT.sub).margin({ top: 2 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 12, bottom: 12 })
Column().width(1).height(36).backgroundColor(LT.line)
Column() {
Text('64').fontSize(22).fontWeight(800).fontColor(LT.primary)
Text('表带').fontSize(11).fontColor(LT.sub).margin({ top: 2 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 12, bottom: 12 })
Column().width(1).height(36).backgroundColor(LT.line)
Column() {
Text('36').fontSize(22).fontWeight(800).fontColor(LT.accent)
Text('护理').fontSize(11).fontColor(LT.sub).margin({ top: 2 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center).padding({ top: 12, bottom: 12 })
}
.width('100%').backgroundColor(LT.card).margin({ top: 8 })
// 功能宫格
Column() {
Text('快捷功能').fontSize(14).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
Row() {
Column() {
Column().width(44).height(44).borderRadius(22).backgroundColor(LT.primary)
Text('定制').fontSize(11).fontColor(LT.text).margin({ top: 4 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
.onClick(() => { this.onCustom() })
Column() {
Column().width(44).height(44).borderRadius(22).backgroundColor(LT.accent)
Text('课堂').fontSize(11).fontColor(LT.text).margin({ top: 4 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
.onClick(() => { this.onCourse() })
Column() {
Column().width(44).height(44).borderRadius(22).backgroundColor(LT.glow)
Text('匠人').fontSize(11).fontColor(LT.text).margin({ top: 4 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
.onClick(() => { this.onCraftsman() })
Column() {
Column().width(44).height(44).borderRadius(22).backgroundColor(LT.dark)
Text('社区').fontSize(11).fontColor(LT.text).margin({ top: 4 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
.onClick(() => { this.onCommunity() })
}
.width('100%')
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
// 热销榜单
Column() {
Row() {
Text('热销榜单').fontSize(14).fontWeight(700).fontColor(LT.dark)
Column().layoutWeight(1)
Text('查看全部').fontSize(12).fontColor(LT.sub)
}
.width('100%').margin({ bottom: 12 })
ForEach(this.rankList, (w: LtWallet, i: number) => {
Row() {
Text(ltTopN(i)).fontSize(18).fontWeight(900).fontColor(i < 3 ? LT.accent : LT.sub).width(28)
Column() {
Text(w.name).fontSize(14).fontWeight(600).fontColor(LT.text)
Text(w.leather + ' · ' + w.color + ' · ' + w.pockets + '卡位').fontSize(11).fontColor(LT.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column() {
Text('已售 ' + w.sold.toString()).fontSize(12).fontColor(LT.accent).fontWeight(700)
Text('库存 ' + w.stock.toString()).fontSize(10).fontColor(LT.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%').padding({ top: 8, bottom: 8 })
.border({ width: { bottom: 1 }, color: LT.line })
.onClick(() => {
this.selName = w.name
this.showDetail = true
})
}, (w: LtWallet) => w.name)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
// 名家横滑
Column() {
Text('匠人作品').fontSize(14).fontWeight(700).fontColor(LT.dark).margin({ bottom: 8 })
Scroll() {
Row({ space: 12 }) {
ForEach(LT_WALLETS.slice(5, 10), (w: LtWallet) => {
Column() {
Column().width(140).height(80).borderRadius(12)
.linearGradient({ angle: 135, colors: [[LT.glow, 0], [LT.primary, 1]] })
.padding(12)
Text(w.name).fontSize(12).fontWeight(600).fontColor(LT.text).margin({ top: 6 })
Text(w.leather + ' · ' + w.tag).fontSize(10).fontColor(LT.accent).margin({ top: 2 })
}
.width(140).alignItems(HorizontalAlign.Start)
}, (w: LtWallet) => w.name)
}
.padding({ left: 4, right: 4 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8, bottom: 8 })
}
.width('100%')
}
.scrollBar(BarState.Off)
.layoutWeight(1)
.width('100%')
// 钱包详情弹框
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Text('皮具详情').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text(ltWalletByName(this.selName).name).fontSize(16).fontWeight(600).fontColor(LT.text).margin({ top: 8 })
Text(ltWalletByName(this.selName).leather + ' · ' + ltWalletByName(this.selName).color + ' · ' + ltWalletByName(this.selName).tag)
.fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('卡位数').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(LT.bg)
Column().width(ltPocketW(ltWalletByName(this.selName).pockets).toString() + '%').height(8).borderRadius(4).backgroundColor(LT.primary)
}
Text(ltWalletByName(this.selName).pockets.toString() + ' 个').fontSize(12).fontColor(LT.text).fontWeight(600).margin({ top: 4 })
}
.width('100%').margin({ top: 16 })
Column() {
Text('尺寸').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(LT.bg)
Column().width(ltWidthW(ltWalletByName(this.selName).size).toString() + '%').height(8).borderRadius(4).backgroundColor(LT.accent)
}
Text(ltWalletByName(this.selName).size.toString() + ' cm').fontSize(12).fontColor(LT.text).fontWeight(600).margin({ top: 4 })
}
.width('100%').margin({ top: 12 })
Row() {
Column() { Text('价格').fontSize(11).fontColor(LT.sub); Text('¥' + ltWalletByName(this.selName).price.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('库存').fontSize(11).fontColor(LT.sub); Text(ltWalletByName(this.selName).stock.toString()).fontSize(14).fontWeight(700).fontColor(LT.text) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('已售').fontSize(11).fontColor(LT.sub); Text(ltWalletByName(this.selName).sold.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showDetail = false })
Column().layoutWeight(1)
Text('加入收藏').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
}
.width('100%').margin({ top: 16 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
}
.width('100%').height('100%')
}
}
// ============ Tab 2: 钱包 ============
@Component
struct LtWalletTab {
@State wave: number = 0
@State list: LtWallet[] = []
@State filterTag: string = '全部'
private timer: number = -1
@State showDetail: boolean = false
@State showAdd: boolean = false
@State showEdit: boolean = false
@State showDelete: boolean = false
@State selName: string = ''
aboutToAppear() {
this.list = ltFilterWallets('全部')
this.timer = setInterval(() => {
this.wave += 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder modalOverlay() {
Column() {
Column().width('100%').height('100%').backgroundColor('rgba(46,26,14,0.62)')
}
.width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
.onClick(() => {
this.showDetail = false
this.showAdd = false
this.showEdit = false
this.showDelete = false
})
}
build() {
Stack() {
Scroll() {
Column() {
// 筛选 chips
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '经典', '入门', '高端', '复古', '工艺'], (t: string) => {
Text(t).fontSize(12).fontColor(this.filterTag === t ? LT.card : LT.text)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.filterTag === t ? LT.primary : LT.bg)
.onClick(() => {
this.filterTag = t
this.list = ltFilterWallets(t)
})
}, (t: string) => t)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
.width('100%')
.height(36)
// 双列网格
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.list, (w: LtWallet, i: number) => {
Column() {
Stack() {
Column().width('100%').height(70).borderRadius(12)
.linearGradient({ angle: 135, colors: [[LT.glow, 0], [LT.primary, 1]] })
Column().width(28).height(28).borderRadius(14).backgroundColor('rgba(245,237,224,0.3)')
.opacity(ltShine(this.wave, i))
}
.width('100%').height(70)
Text(w.name).fontSize(13).fontWeight(700).fontColor(LT.text).margin({ top: 8 })
Text(w.leather + ' · ' + w.color).fontSize(10).fontColor(LT.sub).margin({ top: 2 })
Row() {
Text(w.tag).fontSize(10).fontColor(LT.accent).fontWeight(600)
Column().layoutWeight(1)
Text(w.pockets + '卡位').fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
Row() {
Text('¥').fontSize(10).fontColor(LT.sub)
Text(w.price.toString()).fontSize(14).fontColor(LT.accent).fontWeight(700)
Column().layoutWeight(1)
Text('库存 ' + w.stock.toString()).fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
}
.width('48.5%')
.padding(10)
.backgroundColor(LT.card)
.borderRadius(12)
.margin({ bottom: 8 })
.onClick(() => {
this.selName = w.name
this.showDetail = true
})
}, (w: LtWallet) => w.name)
}
.width('100%').padding({ left: 12, right: 12 })
// 卡位数对比
Column() {
Text('卡位数对比').fontSize(13).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
ForEach(this.list.slice(0, 5), (w: LtWallet, i: number) => {
Row() {
Text(w.name.substring(0, 6)).fontSize(10).fontColor(LT.sub).width(60)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(10).borderRadius(5).backgroundColor(LT.bg)
Column().width(ltPocketW(w.pockets).toString() + '%').height(10).borderRadius(5).backgroundColor(LT.primary)
}
.layoutWeight(1)
Text(w.pockets.toString()).fontSize(10).fontColor(LT.text).fontWeight(600).width(28)
}
.width('100%').margin({ bottom: 6 })
}, (w: LtWallet) => w.name)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
// 价格柱状图
Column() {
Text('价格对比 (¥)').fontSize(13).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
Row({ space: 4 }) {
ForEach(this.list.slice(0, 6), (w: LtWallet, i: number) => {
Column() {
Column().width(24).height(ltPriceW(w.price)).borderRadius(4).backgroundColor(LT.accent)
Text(w.price.toString()).fontSize(8).fontColor(LT.sub).margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
}, (w: LtWallet) => w.name)
}
.width('100%').justifyContent(FlexAlign.SpaceAround)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8, bottom: 8 })
}
.width('100%')
}
.scrollBar(BarState.Off)
.layoutWeight(1)
.width('100%')
// 钱包详情弹框
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Text('钱包详情').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text(ltWalletByName(this.selName).name).fontSize(16).fontWeight(600).fontColor(LT.text).margin({ top: 8 })
Text(ltWalletByName(this.selName).leather + ' · ' + ltWalletByName(this.selName).color + ' · ' + ltWalletByName(this.selName).tag)
.fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('卡位数').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(LT.bg)
Column().width(ltPocketW(ltWalletByName(this.selName).pockets).toString() + '%').height(8).borderRadius(4).backgroundColor(LT.primary)
}
Text(ltWalletByName(this.selName).pockets.toString() + ' 个').fontSize(12).fontColor(LT.text).fontWeight(600).margin({ top: 4 })
}
.width('100%').margin({ top: 16 })
Column() {
Text('尺寸').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(LT.bg)
Column().width(ltWidthW(ltWalletByName(this.selName).size).toString() + '%').height(8).borderRadius(4).backgroundColor(LT.accent)
}
Text(ltWalletByName(this.selName).size.toString() + ' cm').fontSize(12).fontColor(LT.text).fontWeight(600).margin({ top: 4 })
}
.width('100%').margin({ top: 12 })
Row() {
Column() { Text('价格').fontSize(11).fontColor(LT.sub); Text('¥' + ltWalletByName(this.selName).price.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('库存').fontSize(11).fontColor(LT.sub); Text(ltWalletByName(this.selName).stock.toString()).fontSize(14).fontWeight(700).fontColor(LT.text) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('已售').fontSize(11).fontColor(LT.sub); Text(ltWalletByName(this.selName).sold.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showDetail = false })
Column().layoutWeight(1)
Text('编辑').fontSize(12).fontColor(LT.card).padding({ left: 14, right: 14, top: 8, bottom: 8 }).borderRadius(16).backgroundColor(LT.accent)
.onClick(() => { this.showDetail = false; this.showEdit = true })
Text('删除').fontSize(12).fontColor('#FFFFFF').padding({ left: 14, right: 14, top: 8, bottom: 8 }).borderRadius(16).backgroundColor('#CC3333')
.margin({ left: 8 }).onClick(() => { this.showDetail = false; this.showDelete = true })
}
.width('100%').margin({ top: 16 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
// 新增弹框
if (this.showAdd) {
Stack() {
this.modalOverlay()
Column() {
Text('新增钱包').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Column() {
Text('名称').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('请输入钱包名称').fontSize(12).fontColor(LT.sub)
Column().layoutWeight(1)
}
.width('100%').height(36).backgroundColor(LT.bg).borderRadius(8).padding({ left: 12, right: 12 })
}
.width('100%').margin({ top: 16 })
Column() {
Text('皮料').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('马鞍革').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('植鞣革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('油蜡皮').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('编织革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
}
.width('100%').margin({ top: 12 })
Column() {
Text('颜色').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('深棕').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.accent)
Text('焦糖').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('黑棕').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('酒红').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
}
.width('100%').margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showAdd = false })
Column().layoutWeight(1)
Text('确认新增').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
.onClick(() => { this.showAdd = false })
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
// 编辑弹框
if (this.showEdit) {
Stack() {
this.modalOverlay()
Column() {
Text('编辑钱包').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text('当前:' + this.selName).fontSize(12).fontColor(LT.sub).margin({ top: 8 })
Column() {
Text('修改名称').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text(this.selName).fontSize(12).fontColor(LT.text)
Column().layoutWeight(1)
}
.width('100%').height(36).backgroundColor(LT.bg).borderRadius(8).padding({ left: 12, right: 12 })
}
.width('100%').margin({ top: 16 })
Column() {
Text('修改皮料').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('马鞍革').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.accent)
Text('植鞣革').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('油蜡皮').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
}
.width('100%').margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showEdit = false })
Column().layoutWeight(1)
Text('保存修改').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
.onClick(() => { this.showEdit = false })
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
// 删除弹框
if (this.showDelete) {
Stack() {
this.modalOverlay()
Column() {
Text('确认删除').fontSize(18).fontWeight(700).fontColor('#CC3333').margin({ top: 20 })
Text('删除后不可恢复').fontSize(12).fontColor(LT.sub).margin({ top: 8 })
Column() {
Text('删除目标').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Text(this.selName).fontSize(16).fontWeight(700).fontColor(LT.text)
}
.width('100%').padding(16).backgroundColor('#FFF8F0').borderRadius(12)
.border({ width: 1, color: '#E0C0A0' }).margin({ top: 16 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showDelete = false })
Column().layoutWeight(1)
Text('取消').fontSize(13).fontColor(LT.text).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.bg)
.onClick(() => { this.showDelete = false })
Text('确认删除').fontSize(13).fontColor('#FFFFFF').padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor('#CC3333')
.margin({ left: 8 }).onClick(() => { this.showDelete = false })
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
}
.width('100%').height('100%')
}
}
// ============ Tab 3: 包袋 ============
@Component
struct LtBagTab {
@State wave: number = 0
@State list: LtBag[] = []
@State filterTag: string = '全部'
private timer: number = -1
@State showDetail: boolean = false
@State showAdd: boolean = false
@State showEdit: boolean = false
@State selName: string = ''
aboutToAppear() {
this.list = ltFilterBags('全部')
this.timer = setInterval(() => {
this.wave += 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder modalOverlay() {
Column() {
Column().width('100%').height('100%').backgroundColor('rgba(46,26,14,0.62)')
}
.width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(999)
.onClick(() => {
this.showDetail = false
this.showAdd = false
this.showEdit = false
})
}
build() {
Stack() {
Scroll() {
Column() {
// 排行榜
Column() {
Text('包袋热销榜').fontSize(14).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
ForEach(this.list, (b: LtBag, i: number) => {
Row() {
Text(ltTopN(i)).fontSize(20).fontWeight(900).fontColor(i < 3 ? LT.accent : LT.sub).width(28)
Column() {
Column().width(56).height(56).borderRadius(12).backgroundColor(LT.bg)
.border({ width: 1, color: LT.line })
Text(b.type).fontSize(10).fontColor(LT.primary).fontWeight(600).margin({ top: 2 })
}
.width(56).alignItems(HorizontalAlign.Center)
Column() {
Text(b.name).fontSize(14).fontWeight(700).fontColor(LT.text)
Text(b.brand + ' · ' + b.leather).fontSize(10).fontColor(LT.sub).margin({ top: 2 })
Row() {
Text(b.tag).fontSize(10).fontColor(LT.primary).padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(4).backgroundColor(LT.bg)
Column().layoutWeight(1)
Text(b.capacity + 'L').fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
Row() {
Text('¥').fontSize(10).fontColor(LT.sub)
Text(b.price.toString()).fontSize(12).fontColor(LT.accent).fontWeight(700)
Column().layoutWeight(1)
Text(b.weight + 'kg').fontSize(10).fontColor(LT.sub)
}
.width('100%').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1).padding({ left: 12 })
Column() {
Text('已售').fontSize(10).fontColor(LT.sub)
Text(b.sold.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent)
}
.alignItems(HorizontalAlign.End)
}
.width('100%').padding(12).backgroundColor(LT.card).borderRadius(12).margin({ bottom: 8 })
.onClick(() => {
this.selName = b.name
this.showDetail = true
})
}, (b: LtBag) => b.name)
}
.width('100%').padding(16).backgroundColor(LT.bg)
// 容量对比
Column() {
Text('容量对比 (L)').fontSize(13).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
ForEach(this.list.slice(0, 5), (b: LtBag, i: number) => {
Row() {
Text(b.name.substring(0, 6)).fontSize(10).fontColor(LT.sub).width(56)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(10).borderRadius(5).backgroundColor(LT.bg)
Column().width(ltCapacityW(b.capacity).toString() + '%').height(10).borderRadius(5).backgroundColor(LT.primary)
}
.layoutWeight(1)
Text(b.capacity.toString()).fontSize(10).fontColor(LT.text).fontWeight(600).width(28)
}
.width('100%').margin({ bottom: 6 })
}, (b: LtBag) => b.name)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8 })
// 重量对比
Column() {
Text('重量对比 (kg)').fontSize(13).fontWeight(700).fontColor(LT.dark).margin({ bottom: 12 })
Row({ space: 6 }) {
ForEach(this.list, (b: LtBag, i: number) => {
Column() {
Column().width(28).height(b.weight * 40).borderRadius(4).backgroundColor(LT.accent)
Text(b.weight.toString()).fontSize(9).fontColor(LT.sub).margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
}, (b: LtBag) => b.name)
}
.width('100%').justifyContent(FlexAlign.SpaceAround)
}
.width('100%').padding(16).backgroundColor(LT.card).margin({ top: 8, bottom: 8 })
}
.width('100%')
}
.scrollBar(BarState.Off)
.layoutWeight(1)
.width('100%')
// 包袋详情弹框
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Text('包袋详情').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Text(ltBagByName(this.selName).name).fontSize(16).fontWeight(600).fontColor(LT.text).margin({ top: 8 })
Text(ltBagByName(this.selName).leather + ' · ' + ltBagByName(this.selName).type + ' · ' + ltBagByName(this.selName).tag)
.fontSize(12).fontColor(LT.sub).margin({ top: 4 })
Column() {
Text('容量').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(LT.bg)
Column().width(ltCapacityW(ltBagByName(this.selName).capacity).toString() + '%').height(8).borderRadius(4).backgroundColor(LT.primary)
}
Text(ltBagByName(this.selName).capacity.toString() + ' L').fontSize(12).fontColor(LT.text).fontWeight(600).margin({ top: 4 })
}
.width('100%').margin({ top: 16 })
Row() {
Column() { Text('价格').fontSize(11).fontColor(LT.sub); Text('¥' + ltBagByName(this.selName).price.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('重量').fontSize(11).fontColor(LT.sub); Text(ltBagByName(this.selName).weight.toString() + 'kg').fontSize(14).fontWeight(700).fontColor(LT.text) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('库存').fontSize(11).fontColor(LT.sub); Text(ltBagByName(this.selName).stock.toString()).fontSize(14).fontWeight(700).fontColor(LT.text) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() { Text('已售').fontSize(11).fontColor(LT.sub); Text(ltBagByName(this.selName).sold.toString()).fontSize(14).fontWeight(700).fontColor(LT.accent) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('100%').padding(12).backgroundColor(LT.bg).borderRadius(12).margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showDetail = false })
Column().layoutWeight(1)
Text('编辑').fontSize(12).fontColor(LT.card).padding({ left: 14, right: 14, top: 8, bottom: 8 }).borderRadius(16).backgroundColor(LT.accent)
.onClick(() => { this.showDetail = false; this.showEdit = true })
}
.width('100%').margin({ top: 16 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
// 新增弹框
if (this.showAdd) {
Stack() {
this.modalOverlay()
Column() {
Text('新增包袋').fontSize(18).fontWeight(700).fontColor(LT.dark).margin({ top: 20 })
Column() {
Text('名称').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('请输入包袋名称').fontSize(12).fontColor(LT.sub)
Column().layoutWeight(1)
}
.width('100%').height(36).backgroundColor(LT.bg).borderRadius(8).padding({ left: 12, right: 12 })
}
.width('100%').margin({ top: 16 })
Column() {
Text('类型').fontSize(12).fontColor(LT.sub).margin({ bottom: 4 })
Row() {
Text('手提').fontSize(12).fontColor(LT.card).padding(8).borderRadius(8).backgroundColor(LT.primary)
Text('斜挎').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('双肩').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
Text('腰包').fontSize(12).fontColor(LT.text).padding(8).borderRadius(8).backgroundColor(LT.bg).margin({ left: 8 })
}
}
.width('100%').margin({ top: 12 })
Row() {
Text().width(28).height(28).borderRadius(14).backgroundColor(LT.sub).onClick(() => { this.showAdd = false })
Column().layoutWeight(1)
Text('确认新增').fontSize(13).fontColor(LT.card).padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(20).backgroundColor(LT.primary)
.onClick(() => { this.showAdd = false })
}
.width('100%').margin({ top: 20 })
}
.width('86%').backgroundColor(LT.card).borderRadius(16).padding(20)
.constraintSize({ maxHeight: '80%' }).zIndex(1000)
}
.width('100%').height('100%')
}
}
总结

这款手工皮具定制工坊应用充分展示了HarmonyOS ArkTS API 24在构建垂直领域电商应用方面的强大能力。从复古风格的视觉设计到完整的定制功能,从丰富的动画效果到精细的数据可视化,每一个细节都体现了匠心精神。
应用的技术架构具有很强的可复制性。接口先行的类型设计、设计令牌化的主题系统、纯函数驱动的动画引擎、组件化的页面结构、数据驱动的列表渲染,这些技术模式不仅适用于皮具应用,也可以轻松迁移到其他垂直领域的电商应用中。开发者只需调整主题色、数据模型和文案内容,就能快速构建出另一款风格迥异的应用。
更多推荐



所有评论(0)