HarmonyOS 6.1 实战:好评分段柱状图采用了(g.score - 85) / 10 * 90的计算公式
引言

办公文具作为电商市场中B端属性最强的品类之一,其移动端应用开发面临着C端零售与B端采购的双重业务需求。与普通消费品不同,办公用品的采购涉及企业批量询价、增值税开票、月结账期管理、员工领用台账等专业流程,同时还需兼顾学生和个人的零售购买场景。本应用以京东办公文具馆为业务原型,基于HarmonyOS声明式UI开发框架,构建了一个涵盖文具集市、书写工具、办公纸品、桌面收纳、企业采购、消息中心和用户中心七大功能模块的完整电商系统。
从技术架构层面来看,该应用在前两个应用的单页面多标签架构基础上,进一步丰富了弹窗交互的维度。应用引入了批量采购询价弹窗(showBulk)和开票申请弹窗(showInvoice)两个B端专属弹窗,使得弹窗总数达到五个。批量采购弹窗的采购数量采用了步进10的递增递减设计(范围10-5000),体现了企业采购的批量特征。开票弹窗集成了发票类型选择(电子普票、纸质普票、增值税专票)和发票抬头选择(个人、企业、单位),覆盖了企业采购中最核心的开票场景。所有商品数据同样通过interface接口进行类型约束,GoodsItem的num字段在文具场景中表示笔尖规格或纸张克重,展现了数据模型设计的持续复用能力。
从业务设计层面来看,该应用深度还原了办公文具行业的核心业务流程。头部区域展示开学季促销信息和企业会员专属折扣,通过双层级优惠吸引用户;书写工具页面引入笔尖规格柱状图,通过可视化手段帮助用户理解不同笔尖的粗细差异;办公纸品页面使用纸张克重分布柱状图和复购进度条列表,为办公用品的批量采购提供了数据支撑。企业采购模块是应用的核心特色,展示了综合好评率、服务企业数量和最快账期等关键指标,并提供从定制文具到考勤机的完整集采服务目录。
从交互设计层面来看,应用在文本处理上大量使用了slice方法进行字符串截取。例如"次日达 · 破损包退".slice(0, 10)、"好评率 ".slice(0, 1)等,这种技术在移动端UI中用于控制文本长度和适配不同屏幕宽度。此外,桌面收纳页面的好评分段柱状图采用了(g.score - 85) / 10 * 90的计算公式,将评分从85分基线开始分段显示,使得90分和95分的差异在柱状图中更加明显,体现了数据可视化中的细节优化思维。
数据模型与接口定义

应用通过三个接口定义了核心数据模型,与前面两个应用保持了一致的结构设计。GoodsItem接口描述商品实体,TabInfo接口定义导航标签,MsgItem接口规范消息通知结构。这种统一的接口设计使得三个应用共享同一套数据驱动逻辑,体现了良好的架构复用性。
interface GoodsItem {
name: string
price: number
desc: string
score: number
num: number
tag: string
hot: boolean
}
interface TabInfo {
name: string
icon: string
interface MsgItem {
title: string
content: string
time: string
read: boolean
}
在文具应用中,GoodsItem的num字段承担了新的业务语义:在书写工具列表中表示笔尖规格数值(如5表示0.5mm、4表示0.4mm),在办公纸品列表中表示纸张克重(如70表示70g、80表示80g),在桌面收纳列表中复用为评分基数。tag字段在书写工具中存储了笔尖规格的完整描述(如"笔尖0.5mm"),通过g.tag.slice(2, 7)可以提取出"0.5mm"部分用于柱状图标注。
状态管理与组件初始化

根组件Index通过@State装饰器声明了响应式状态变量。与前两个应用相比,文具应用引入了采购数量(bulkNum)、发票类型选择(selInvType)和发票抬头选择(selTitle)三个B端业务专属状态。
@Entry
@Component
struct Index {
@State currentTab: number = 0
@State showPublish: boolean = false
@State showBulk: boolean = false
@State showInvoice: boolean = false
@State showDelete: boolean = false
@State showDetail: boolean = false
@State selectedItem: GoodsItem | null = null
@State selCat: number = 0
@State selUse: number = 0
@State selSpec: number = 0
@State bulkNum: number = 100
@State selInvType: number = 0
@State selTitle: number = 0
@State deleteIndex: number = -1
@State msgs: MsgItem[] = [
{ title: '采购审批通过', content: '您提交的企业采购单已审批通过,预计 2 日内发货', time: '10:36', read: false },
{ title: '开票通知', content: '订单尾号 8821 的电子发票已开具,请查收邮箱', time: '09:48', read: false },
{ title: '补货提醒', content: '您常购的 A4 纸库存不足一箱,建议及时补货', time: '昨天', read: false },
{ title: '物流更新', content: '您的文具整箱已从广州仓发出,预计后天送达', time: '昨天', read: true },
{ title: '企业专属价', content: '本月企业会员价已更新,打印机耗材直降 15%', time: '前天', read: true },
{ title: '对账单生成', content: '8 月采购对账单已生成,可在企业中心下载', time: '前天', read: true },
{ title: '新品上架', content: '进口中性笔新款到货,0.5mm 针管头书写顺滑', time: '周三', read: true },
{ title: '直播预告', content: '明晚 19:00 办公焕新专场直播,整点抽打印机', time: '周三', read: true },
{ title: '优惠券到账', content: '企业采购满 1000 减 120 券已发放,30 天有效', time: '周四', read: true },
{ title: '评价有礼', content: '评价采购的办公椅可领 30 元无门槛券', time: '上周', read: true },
{ title: '以旧换新', content: '旧打印机折价回收,最高抵 800 元', time: '上周', read: true },
{ title: '安全提醒', content: '谨防冒充供应商的变更收款账号诈骗', time: '上周', read: true }
]
消息列表的内容深度体现了B端业务特征——包含采购审批通过、开票通知、补货提醒、对账单生成等企业采购专属通知。bulkNum的初始值为100,对应企业采购的典型起订量。selInvType和selTitle分别用于开票弹窗中的发票类型和抬头选择,初始值均为0表示默认选择第一项。
私有数据与商品列表

组件内部通过private声明了配置选项和五组商品数据。配置选项包括书写工具品类、适用场景、规格选择、发票类型和发票抬头五组数组。商品数据按品类分为综合精选items、书写工具pens、办公纸品papers、桌面收纳desks和企业采购corps五组。
private tabs: TabInfo[] = [
{ name: '文具集市', icon: '✏️' },
{ name: '书写工具', icon: '🖊️' },
{ name: '办公纸品', icon: '📄' },
{ name: '桌面收纳', icon: '🗄️' },
{ name: '企业采购', icon: '🏢' },
{ name: '消息', icon: '✉️' },
{ name: '我的', icon: '👤' }
]
private cats: string[] = ['中性笔', '钢笔', '荧光笔', '铅笔', '马克笔', '白板笔']
private uses: string[] = ['学生刷题', '办公签字', '教师批改', '绘画手账']
private specs: string[] = ['0.38mm 黑', '0.5mm 黑', '0.5mm 蓝', '12 色混装']
private invTypes: string[] = ['电子普票', '纸质普票', '增值税专票']
private titles: string[] = ['个人抬头', '企业抬头', '单位抬头']
private items: GoodsItem[] = [
{ name: '百乐P500中性笔0.5', price: 9, desc: '直液式 大容量顺滑', score: 96, num: 5, tag: '刷题神器', hot: true },
{ name: '三菱UM-151中性笔', price: 8, desc: '低粘度油墨 快干', score: 95, num: 5, tag: '经典款', hot: false },
{ name: '晨光优品按动笔', price: 3, desc: '国货性价比 顺滑不断墨', score: 93, num: 5, tag: '性价比', hot: true },
{ name: '百乐78G+钢笔', price: 68, desc: 'F尖 磨砂手感 练字', score: 95, num: 3, tag: '入门钢笔', hot: false },
{ name: '写乐四季织钢笔', price: 328, desc: '透明杆 15度尖', score: 96, num: 3, tag: '进阶款', hot: false },
{ name: '凌美狩猎者钢笔', price: 79, desc: '德系经典 ABS笔杆', score: 94, num: 3, tag: '德系', hot: true },
{ name: '斑马JJ15复古色', price: 7, desc: '复古五色 手账配色', score: 94, num: 5, tag: '手账', hot: false },
{ name: '无印双头荧光笔', price: 12, desc: '不透纸 温和色系', score: 93, num: 5, tag: '划重点', hot: false },
{ name: '施德楼马克笔套装', price: 128, desc: '专业设计 24色', score: 95, num: 5, tag: '设计师', hot: false },
{ name: '辉柏嘉水彩铅笔', price: 89, desc: '36色 水溶晕染', score: 94, num: 5, tag: '绘画', hot: false },
{ name: '得力A4复印纸整箱', price: 118, desc: '70g 500张×8包', score: 95, num: 70, tag: '办公刚需', hot: true },
{ name: '晨光笔记本B5本', price: 6, desc: '康奈尔内页 加厚', score: 93, num: 5, tag: '学生党', hot: false },
{ name: '国誉Campus笔记本', price: 15, desc: '东大脑训练格', score: 95, num: 5, tag: '进口', hot: false },
{ name: '得力订书机大号', price: 25, desc: '可订50页 省力', score: 92, num: 5, tag: '办公', hot: false },
{ name: '得力文件夹加厚', price: 4, desc: 'A4双强夹 透明', score: 91, num: 5, tag: '归档', hot: false },
{ name: '桌面文件收纳架', price: 45, desc: '三层分格 竖立收纳', score: 93, num: 5, tag: '收纳', hot: true },
{ name: '透明抽屉收纳盒', price: 35, desc: '五层小抽屉', score: 92, num: 5, tag: '桌面', hot: false },
{ name: '便签纸组合装', price: 16, desc: '76×76 四色各一本', score: 91, num: 5, tag: '便签', hot: false },
{ name: '白板磁吸大号', price: 129, desc: '90×120cm 免打孔', score: 94, num: 5, tag: '会议室', hot: false },
{ name: '人体工学办公椅', price: 899, desc: '腰托可调 网布透气', score: 95, num: 5, tag: '久坐党', hot: true },
{ name: '升降桌电动款', price: 1599, desc: '记忆高度 双电机', score: 96, num: 5, tag: '站坐交替', hot: false },
{ name: '桌面理线器磁吸', price: 19, desc: '告别线材凌乱', score: 90, num: 5, tag: '神器', hot: false },
{ name: '洞洞板桌面置物', price: 59, desc: '自由组合 收纳上墙', score: 92, num: 5, tag: '改造', hot: false },
{ name: '墨囊补给装100枚', price: 22, desc: '通用型 黑蓝红', score: 93, num: 5, tag: '耗材', hot: false }
]
综合商品列表覆盖了从3元的晨光按动笔到1599元的电动升降桌,价格梯度极大,兼顾了学生个人用户和企业采购用户。书写工具列表pens的tag字段存储了完整的笔尖规格信息(如"笔尖0.5mm"),这使得柱状图标注可以通过slice方法提取规格数值。企业采购列表corps的num字段用于"已合作 X 家"的计算(g.num * 36),通过乘以固定系数模拟了服务企业数量的累加效果。
头部构建与双层级促销

buildHeader方法构建了应用顶部导航区域,采用了深蓝色#283593作为主背景色,传达了办公用品的专业商务感。头部整合了品牌标识、企业采购服务标签、开票入口和消息中心入口,底部还展示了开学季促销信息条。
@Builder buildHeader() {
Column() {
Row() {
Column() {
Text('京东办公文具馆').fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold)
Row() {
Text('🏢').fontSize(11)
Text('企业采购 · 对公转账 · 增值税开票').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ left: 4 })
}
.margin({ top: 4 })
.alignItems(VerticalAlign.Center)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Column() {
Text('📄').fontSize(18)
Text('开票').fontSize(9).fontColor('rgba(255,255,255,0.8)').margin({ top: 2 })
}
.width(44)
.onClick(() => {
this.selInvType = 0
this.selTitle = 0
this.showInvoice = true
})
Column() {
Text('✉️').fontSize(18)
if (this.unreadCount() > 0) {
Text(this.unreadCount().toString())
.fontSize(8)
.fontColor(Color.White)
.backgroundColor('#FF3B30')
.borderRadius(7)
.padding({ left: 4, right: 4 })
.position({ x: 12, y: -4 })
.scale({ x: 1.12, y: 1.12 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.width(44)
.onClick(() => {
this.currentTab = 5
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 12, bottom: 12 })
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('开学季 · 文具 5 折起').fontSize(11).fontColor('#E3F2FD')
Text('企业会员再享折上 95 折').fontSize(10).fontColor('rgba(255,255,255,0.75)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
Column() {
Text('领券').fontSize(11).fontColor('#283593').backgroundColor('#FFFFFF').borderRadius(12).padding({ left: 12, right: 12, top: 4, bottom: 4 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
.margin({ left: 12 })
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 12 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.backgroundColor('#283593')
}
与前两个应用的显著区别在于:头部右侧的第一个快捷入口是"开票"而非品类图标,直接突出B端开票业务的重要性。点击开票入口时会重置selInvType和selTitle为0并打开开票弹窗。促销信息条采用了双层级设计——上层"开学季 · 文具 5 折起"面向C端学生用户,下层"企业会员再享折上 95 折"面向B端企业用户,通过信息分层同时服务两类用户群体。"领券"按钮使用白底蓝字设计,与深蓝头部形成对比,配有1.05倍脉冲动画引导点击。
文具集市与开学季活动

buildTab1方法构建了文具集市的商品列表页。页面顶部是开学焕新季活动横幅,配有"368万+师生已选购"的社交证明数据。下方商品列表使用了铅笔、钢笔、纸张和收纳柜四种表情符号循环交替,热销标签显示为"回购率高",配合脉冲动画吸引用户关注。
@Builder buildTab1() {
Column() {
Row() {
Column() {
Text('📚').fontSize(28)
Column() {
Text('开学焕新季').fontSize(15).fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('文具 5 折 · 笔记本买二送一').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
}
.alignItems(HorizontalAlign.Center)
.layoutWeight(1)
Column() {
Text('368万+').fontSize(15).fontColor('#FF7043').fontWeight(FontWeight.Bold)
Text('师生已选购').fontSize(9).fontColor('rgba(255,255,255,0.7)').margin({ top: 3 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor('#3949AB')
.borderRadius(14)
.padding(16)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(VerticalAlign.Center)
Row() {
Text('文具精选').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('次日达 · 破损包退'.slice(0, 10)).fontSize(10).fontColor('#9AA0B5').margin({ left: 8 })
}
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
ForEach(this.items, (g: GoodsItem, i: number) => {
Row() {
Column() {
Text(i % 4 === 0 ? '✏️' : (i % 4 === 1 ? '🖊️' : (i % 4 === 2 ? '📄' : '🗄️'))).fontSize(26)
}
.width(72)
.height(72)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#E3F2FD')
.justifyContent(FlexAlign.Center)
Column() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Row() {
Text(g.tag).fontSize(9).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 })
if (g.hot) {
Text('回购率高'.slice(0, 4)).fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.06, y: 1.06 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.margin({ top: 5 })
.alignItems(VerticalAlign.Center)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(6).backgroundColor('#F0F0F5').borderRadius(3)
Column().width(g.score + '%').height(6).backgroundColor('#FF7043').borderRadius(3)
}
.width('100%')
.margin({ top: 6 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(16).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('批量采购').fontSize(10).fontColor(Color.White)
}
.backgroundColor('#283593')
.borderRadius(13)
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.margin({ top: 8 })
.onClick(() => {
this.selectedItem = g
this.bulkNum = 100
this.showBulk = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ left: 12, right: 12, bottom: 10 })
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ bottom: 12 })
}
活动横幅使用了比头部更浅的蓝色#3949AB,形成同色系的层次感。"368万+师生已选购"的社交证明数据配有1.05倍脉冲动画,通过数据背书增强用户信任。商品标签使用蓝色#283593背景而非前两个应用的品类色,评分进度条使用橙色#FF7043填充。价格文字使用深橙色#D84315而非前两个应用的红色#B71C1C,整体色彩偏向冷色调商务风格。"批量采购"按钮点击后会重置采购数量为100(企业采购典型起订量)并打开批量询价弹窗。
书写工具与笔尖规格可视化

buildTab2方法构建了书写工具页面,核心特色是笔尖规格柱状图和双列精选列表。柱状图使用深蓝色#283593柱体展示各笔尖的规格数值,标签通过g.tag.slice(2, 7)从"笔尖0.5mm"格式中提取出"0.5mm"部分。
@Builder buildTab2() {
Column() {
Text('笔尖规格柱状图(mm×10)').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
Column() {
Row({ space: 10 }) {
ForEach(this.pens, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height(g.num / this.maxOfNum(this.pens) * 100)
.backgroundColor('#283593')
.borderRadius(4)
Text(g.tag.slice(2, 7)).fontSize(7).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.alignItems(VerticalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12 })
.alignItems(HorizontalAlign.Start)
Text('书写工具双列精选').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.pens, (g: GoodsItem, i: number) => {
Column() {
Row() {
Column() {
Text('🖊️').fontSize(22)
}
.width(44)
.height(44)
.borderRadius(22)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#FFF3E0')
.justifyContent(FlexAlign.Center)
Column() {
Text(g.tag).fontSize(9).fontColor('#283593').fontWeight(FontWeight.Bold)
Text('好评 ' + g.score + '%').fontSize(9).fontColor('#8A8FA3').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
Text(g.name).fontSize(12).fontColor('#212121').fontWeight(FontWeight.Medium).margin({ top: 8 }).maxLines(1)
Text(g.desc).fontSize(9).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Row() {
Text('¥' + g.price).fontSize(14).fontColor('#D84315').fontWeight(FontWeight.Bold)
if (g.hot) {
Text('编辑推荐').fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}
.width('100%')
.margin({ top: 6 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
}
.width('48%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
}
.width('100%')
.padding({ bottom: 12 })
}
笔尖规格柱状图的标题中"(mm×10)“表示数值已乘以10进行放大显示——num字段存储的是5(代表0.5mm),柱状图直接使用该数值进行高度计算。g.tag.slice(2, 7)从"笔尖0.5mm"中提取"0.5mm”,这种字符串截取技术在标签提取中非常实用。双列精选列表的卡片设计采用圆形图标加右侧标签的并排布局,热销标签显示为"编辑推荐"而非通用的"热销",体现了文具品类的专业推荐属性。圆形图标背景色交替使用淡蓝#E8EAF6和淡橙#FFF3E0,冷色调和暖色调的交替增加了视觉趣味。
办公纸品与克重分布

buildTab3方法构建了办公纸品页面,融合了纸张克重分布柱状图、口碑排行复购进度条列表和横向滑动的本册速览三个板块。克重柱状图使用浅橙色#FFAB91柱体,在深蓝色#1A237E背景上形成鲜明对比。
@Builder buildTab3() {
Column() {
Column() {
Text('纸张克重分布柱状图(g)').fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Bold)
Row({ space: 10 }) {
ForEach(this.papers, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height(g.num / this.maxOfNum(this.papers) * 100)
.backgroundColor('#FFAB91')
.borderRadius(4)
Text(g.num.toString()).fontSize(8).fontColor('rgba(255,255,255,0.85)').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.margin({ top: 12 })
.alignItems(VerticalAlign.End)
}
.width('100%')
.backgroundColor('#1A237E')
.borderRadius(14)
.padding(14)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(HorizontalAlign.Start)
Text('口碑排行 · 复购进度').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Column() {
ForEach(this.papers, (g: GoodsItem, i: number) => {
Row() {
Text((i + 1).toString()).fontSize(12).fontColor(Color.White).backgroundColor(i < 3 ? '#FF7043' : '#9AA0B5').borderRadius(10).width(20).height(20).textAlign(TextAlign.Center)
Column() {
Text(g.name).fontSize(12).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(7).backgroundColor('#F0F0F5').borderRadius(4)
Column().width(g.score + '%').height(7).backgroundColor('#283593').borderRadius(4)
}
.width('100%')
.margin({ top: 5 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold)
if (g.hot) {
Text('🔥 囤货王').fontSize(8).fontColor('#D84315').margin({ top: 3 })
.scale({ x: 1.1, y: 1.1 })
.animation({ duration: 600, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
.margin({ bottom: 12 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12 })
Text('本册速览 · 横滑'.slice(0, 8)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Scroll() {
Row({ space: 10 }) {
ForEach(this.papers, (g: GoodsItem, i: number) => {
Column() {
Column() {
Text('📄').fontSize(28)
}
.width(76)
.height(76)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E3F2FD' : '#E8EAF6')
.justifyContent(FlexAlign.Center)
Text(g.name).fontSize(11).fontColor('#212121').margin({ top: 8 }).maxLines(1)
Text('¥' + g.price).fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold).margin({ top: 4 })
}
.width(112)
.backgroundColor(Color.White)
.borderRadius(12)
.padding(10)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
.width('100%')
}
.width('100%')
.padding({ bottom: 12 })
}
口碑排行列表的前三名序号使用橙色#FF7043背景,与克重柱状图的柱体颜色形成呼应。复购进度条使用蓝色#283593填充,保持了品牌色的一致性。热销标签显示为"囤货王"并配有1.1倍脉冲动画和火焰图标,这种趣味化的标签文案增强了用户的购买冲动。横向滑动区的本册卡片宽度为112像素,比酒水应用的120像素更窄,适应了纸品商品信息更简洁的展示需求。
应用核心流程
企业采购中心与集采服务
buildTab5方法是应用最具B端特色的功能页面,构建了一个完整的企业采购中心界面。页面顶部展示了采购数据面板——综合好评率、服务企业数量和最快账期,配以办公楼图标动画。下方是集采服务项目列表,涵盖从定制文具到考勤机的完整产品线。
@Builder buildTab5() {
Column() {
Column() {
Row() {
Column() {
Text('企业采购中心').fontSize(17).fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('对公转账 · 月结账期 · 专属客户经理'.slice(0, 15)).fontSize(10).fontColor('rgba(255,255,255,0.75)').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('🏢').fontSize(32)
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
}
.width('100%')
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('86%').fontSize(22).fontColor('#FFAB91').fontWeight(FontWeight.Bold)
Text('综合好评').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text('3200+').fontSize(22).fontColor('#FFF59D').fontWeight(FontWeight.Bold).margin({ top: 2 })
.scale({ x: 1.04, y: 1.04 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
Text('服务企业').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text('T+7').fontSize(22).fontColor('#80CBC4').fontWeight(FontWeight.Bold).margin({ top: 2 })
Text('最快账期').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.margin({ top: 16 })
.alignItems(VerticalAlign.Top)
}
.width('100%')
.backgroundColor('#283593')
.borderRadius(14)
.padding(18)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(HorizontalAlign.Start)
Text('集采服务项目'.slice(0, 6)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Column() {
ForEach(this.corps, (g: GoodsItem, i: number) => {
Row() {
Column() {
Text(i % 5 === 0 ? '🏢' : (i % 5 === 1 ? '🧾' : (i % 5 === 2 ? '🖨️' : (i % 5 === 3 ? '🪴' : '🪑')))).fontSize(24)
}
.width(52)
.height(52)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#E0F2F1')
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.tag).fontSize(9).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
if (g.hot) {
Text('客户点赞'.slice(0, 4)).fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(VerticalAlign.Center)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Text('好评率 ' + g.score + '% · 已合作 ' + g.num * 36 + ' 家').fontSize(9).fontColor('#9AA0B5').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price + ' 起').fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('批量采购'.slice(0, 4)).fontSize(10).fontColor(Color.White)
}
.backgroundColor('#283593')
.borderRadius(13)
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.margin({ top: 6 })
.onClick(() => {
this.selectedItem = g
this.bulkNum = 100
this.showBulk = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.alignItems(VerticalAlign.Center)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
}
.width('100%')
.padding({ bottom: 12 })
}
企业采购数据面板使用了深蓝色#283593背景与头部品牌色一致。三个数据指标采用了不同的强调色:综合好评率使用浅橙#FFAB91,服务企业数量使用浅黄#FFF59D并配有1.04倍脉冲动画,最快账期使用浅青#80CBC4。通过不同颜色的数据展示,用户可以快速区分不同维度的指标。“已合作 " + g.num * 36 + " 家"的计算公式将num字段乘以固定系数36,模拟了企业服务数量的累加效果。集采服务列表的图标使用了五种表情符号循环(办公楼、发票、打印机、绿植、椅子),覆盖了企业办公场景的主要品类。热销标签显示为"客户点赞”,强调了B端客户口碑的重要性。
批量采购询价弹窗
bulkModal方法是B端业务的核心弹窗,集成了规格选择器、采购数量步进器和实时总价计算器。采购数量采用步进10的递增递减设计(范围10-5000),总价按selPrice() * bulkNum实时计算,并显示批量92折优惠信息。
@Builder bulkModal() {
Column() {
Column() {
Row() {
Column() {
Text('批量采购询价'.slice(0, 6)).fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('量大从优 · 免费开票 · 月结可选'.slice(0, 14)).fontSize(10).fontColor('#283593').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
Text('✕').fontSize(16).fontColor('#9AA0B5')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Column() {
Text(this.selName()).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(this.selDesc()).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Column() {
Text('规格选择'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.specs, (s: string, i: number) => {
Text(s).fontSize(11).fontColor(this.selSpec === i ? Color.White : '#5C6280')
.backgroundColor(this.selSpec === i ? '#283593' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selSpec = i
})
}, (s: string) => s)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Row() {
Column() {
Text('采购数量(件)'.slice(0, 7)).fontSize(11).fontColor('#5C6280')
Row() {
Text('-').fontSize(15).fontColor('#283593').width(28).height(28).textAlign(TextAlign.Center).backgroundColor('#F5F6FA').borderRadius(8)
.onClick(() => {
if (this.bulkNum > 10) {
this.bulkNum = this.bulkNum - 10
}
})
Text(this.bulkNum.toString()).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).width(60).textAlign(TextAlign.Center)
Text('+').fontSize(15).fontColor('#283593').width(28).height(28).textAlign(TextAlign.Center).backgroundColor('#F5F6FA').borderRadius(8)
.onClick(() => {
if (this.bulkNum < 5000) {
this.bulkNum = this.bulkNum + 10
}
})
}
.margin({ top: 6 })
.alignItems(VerticalAlign.Center)
}
.alignItems(HorizontalAlign.Start)
Column() {
Text('预估总价'.slice(0, 4)).fontSize(11).fontColor('#5C6280')
Text('¥' + (this.selPrice() * this.bulkNum)).fontSize(22).fontColor('#D84315').fontWeight(FontWeight.Bold).margin({ top: 6 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text('已享批量 92 折'.slice(0, 9)).fontSize(9).fontColor('#2E7D32').margin({ top: 3 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 16 })
.alignItems(VerticalAlign.Top)
Row() {
Column() {
Text('仅询价'.slice(0, 3)).fontSize(11).fontColor('#8A8FA3')
}
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.border({ width: 1, color: '#E5E5EC' })
.borderRadius(12)
.onClick(() => {
this.showBulk = false
})
Column() {
Text('提交采购单'.slice(0, 5)).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.height(40)
.backgroundColor('#283593')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
this.showBulk = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(16)
.constraintSize({ maxHeight: '80%' })
.onClick((event: ClickEvent) => {
event.stopPropagation()
})
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.End)
.onClick(() => {
this.showBulk = false
})
}
批量采购弹窗从底部弹出(justifyContent: FlexAlign.End),符合B端快速操作的习惯。采购数量的步进设计是最大亮点——每次增减10件而非1件,范围限制为10-5000,这直接反映了企业采购的批量特征。递减时检查bulkNum > 10,递增时检查bulkNum < 5000,确保了数量在合理范围内。预估总价配有1.05倍脉冲动画,"已享批量 92 折"以绿色#2E7D32显示,通过优惠信息激励用户增加采购量。底部双按钮设计中,"仅询价"使用描边样式作为轻量操作,"提交采购单"使用蓝色实心样式作为正式操作。
开票申请弹窗
invoiceModal方法是应用中B端属性最强的弹窗,集成了发票类型选择、发票抬头选择和开票金额区间展示三大功能。发票类型覆盖了电子普票、纸质普票和增值税专票三种,抬头覆盖了个人、企业和单位三种。
@Builder invoiceModal() {
Column() {
Column() {
Row() {
Column() {
Text('开票申请').fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('电子发票 30 秒极速开具'.slice(0, 12)).fontSize(10).fontColor('#00897B').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
Text('✕').fontSize(16).fontColor('#9AA0B5')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Column() {
Text('发票类型'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.invTypes, (t: string, i: number) => {
Text(t).fontSize(11).fontColor(this.selInvType === i ? Color.White : '#5C6280')
.backgroundColor(this.selInvType === i ? '#283593' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selInvType = i
})
}, (t: string) => t)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 16 })
Column() {
Text('发票抬头'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.titles, (t: string, i: number) => {
Text(t).fontSize(11).fontColor(this.selTitle === i ? Color.White : '#5C6280')
.backgroundColor(this.selTitle === i ? '#00897B' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selTitle = i
})
}, (t: string) => t)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Column() {
Text('开票金额区间'.slice(0, 6)).fontSize(12).fontColor('#5C6280')
Row({ space: 8 }) {
Text('¥100-500').fontSize(11).fontColor('#5C6280').backgroundColor('#F5F6FA').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 })
Text('¥500-2000').fontSize(11).fontColor('#5C6280').backgroundColor('#F5F6FA').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 })
Text('¥2000+').fontSize(11).fontColor(Color.White).backgroundColor('#D84315').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 }).margin({ left: 8 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
.margin({ top: 8 })
.alignItems(VerticalAlign.Center)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Row() {
Column() {
Text('取消'.slice(0, 2)).fontSize(13).fontColor('#5C6280')
}
.layoutWeight(1)
.height(40)
.backgroundColor('#F5F6FA')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showInvoice = false
})
Column() {
Text('提交开票'.slice(0, 4)).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.layoutWeight(1.6)
.height(40)
.backgroundColor('#00897B')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
this.showInvoice = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('92%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(16)
.constraintSize({ maxHeight: '80%' })
.onClick((event: ClickEvent) => {
event.stopPropagation()
})
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showInvoice = false
})
}
开票弹窗从中央弹出(justifyContent: FlexAlign.Center),与其他B端弹窗从底部弹出不同,体现了开票操作的正式性和审慎性。两个选择器采用了不同的选中颜色:发票类型选中为蓝色#283593(品牌色),发票抬头选中为绿色#00897B(财税色),通过颜色差异帮助用户区分不同维度的选择。开票金额区间使用了三个标签展示,其中"¥2000+"使用橙色#D84315背景配1.08倍脉冲动画,强调了高金额开票的引导意图。提交按钮使用了绿色#00897B背景而非品牌蓝色,因为绿色在财税场景中代表"确认通过"的含义。
主构建函数与弹窗编排
build方法作为应用入口构建函数,通过Stack容器将主界面与五个弹窗叠加。整体背景色使用了#F4F6FB——一种偏冷的浅蓝灰色,比前两个应用的暖色调背景更符合办公商务的视觉调性。
build() {
Stack() {
Column() {
this.buildHeader()
Scroll() {
Column() {
if (this.currentTab === 0) {
this.buildTab1()
} else if (this.currentTab === 1) {
this.buildTab2()
} else if (this.currentTab === 2) {
this.buildTab3()
} else if (this.currentTab === 3) {
this.buildTab4()
} else if (this.currentTab === 4) {
this.buildTab5()
} else if (this.currentTab === 5) {
this.buildTab6()
} else {
this.buildTab7()
}
}
.width('100%')
}
.layoutWeight(1)
.scrollBar(BarState.Off)
.width('100%')
.align(Alignment.Top)
this.buildBottom()
}
.width('100%')
.height('100%')
if (this.showPublish) {
this.publishModal()
}
if (this.showBulk) {
this.bulkModal()
}
if (this.showInvoice) {
this.invoiceModal()
}
if (this.showDelete) {
this.deleteModal()
}
if (this.showDetail) {
this.detailModal()
}
}
.width('100%')
.height('100%')
.backgroundColor('#F4F6FB')
}
底部导航栏的选中状态颜色为蓝色#283593,与头部品牌色一致。弹窗的渲染顺序为:发布闲置、批量采购、开票申请、删除确认、商品详情。前三个弹窗是B端专属功能,后两个是通用功能,这种排列顺序确保了B端弹窗在渲染层级上的优先性。五个弹窗通过独立的@State布尔变量控制显示,互斥的设计避免了多个弹窗同时出现的混乱情况。
桌面收纳好评分段柱状图
buildTab4方法构建了桌面收纳页面,其中好评分段柱状图采用了独特的计算公式(g.score - 85) / 10 * 90。这个公式将评分从85分基线开始计算,除以10得到分数段比例,再乘以90像素的最大高度,使得90-100分范围内的评分差异在柱状图中被放大显示。
@Builder buildTab4() {
Column() {
Text('桌面收纳专区').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
Column() {
ForEach(this.desks, (g: GoodsItem, i: number) => {
Row() {
Column()
.width(4)
.height(48)
.backgroundColor(i % 4 === 0 ? '#283593' : (i % 4 === 1 ? '#FF7043' : (i % 4 === 2 ? '#00897B' : '#5C6BC0')))
.borderRadius(2)
Column() {
Row() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.tag).fontSize(9).fontColor('#00897B').backgroundColor('#E0F2F1').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
if (g.hot) {
Text('热销').fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(VerticalAlign.Center)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Text('好评率 ' + g.score + '%'.slice(0, 1) + ')').fontSize(9).fontColor('#9AA0B5').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(15).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('查看详情'.slice(0, 4)).fontSize(9).fontColor('#283593')
}
.borderRadius(11)
.border({ width: 1, color: '#283593' })
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.margin({ top: 6 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
Text('好评分段柱状图'.slice(0, 7)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 10, bottom: 8 })
Column() {
Row({ space: 12 }) {
ForEach(this.desks, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height((g.score - 85) / 10 * 90)
.backgroundColor('#00897B')
.borderRadius(4)
Text(g.score.toString()).fontSize(8).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.alignItems(VerticalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12, top: 4 })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding({ bottom: 12 })
}
桌面收纳列表的左侧色条使用了四色循环(蓝色、橙色、绿色、蓝灰色),比前两个应用的三色循环更丰富。标签使用绿色#00897B背景配浅绿#E0F2F1底色,区别于其他页面的蓝色标签。"查看详情"按钮使用了描边样式而非实心背景,作为轻量级操作入口。好评分段柱状图的(g.score - 85) / 10 * 90公式是数据可视化中的分段放大技术——当所有评分都在90-98范围内时,直接使用评分值作为柱高会导致差异不明显,通过减去85分基线再除以10进行比例放大,使得93分和96分的柱高差异从3%变成了30%,大幅提升了可视化效果。
技术点对比分析
| 技术维度 | 实现方式 | 设计特点 | 适用场景 |
|---|---|---|---|
| 状态管理 | @State+B端业务状态 | 采购数量、发票类型、抬头选择 | 批量采购、开票申请 |
| 步进调节 | bulkNum±10步进 | 范围10-5000,步进10件 | 企业批量采购数量选择 |
| 字符串截取 | slice方法广泛使用 | 控制文本长度、提取标签数值 | 标签展示、标题截断 |
| 分段柱状图 | (score-85)/10*90 | 减去基线放大差异 | 桌面收纳好评分段图 |
| 双色选择器 | 类型蓝+抬头绿 | 颜色区分不同选择维度 | 开票弹窗双维度选择 |
| 弹窗弹出 | 居中vs底部差异化 | 开票居中、采购底部 | B端弹窗交互定位 |
| 数据面板 | 多色数据指标 | 好评橙、企业黄、账期青 | 企业采购中心 |
| 估值计算 | num*36固定系数 | 模拟企业服务数量累加 | 集采服务合作数展示 |
| 动画效果 | scale+animation脉冲 | iterations:-1无限循环 | 领券按钮、未读角标、总价 |
| 事件处理 | onClick+stopPropagation | 遮罩关闭+内容阻止冒泡 | 所有弹窗交互 |
安装DevEco Studio程序

选择目标安装目录:

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

新建一个空白模板:

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

完整代码:
interface GoodsItem {
name: string
price: number
desc: string
score: number
num: number
tag: string
hot: boolean
}
interface TabInfo {
name: string
icon: string
}
interface MsgItem {
title: string
content: string
time: string
read: boolean
}
@Entry
@Component
struct Index {
@State currentTab: number = 0
@State showPublish: boolean = false
@State showBulk: boolean = false
@State showInvoice: boolean = false
@State showDelete: boolean = false
@State showDetail: boolean = false
@State selectedItem: GoodsItem | null = null
@State selCat: number = 0
@State selUse: number = 0
@State selSpec: number = 0
@State bulkNum: number = 100
@State selInvType: number = 0
@State selTitle: number = 0
@State deleteIndex: number = -1
@State msgs: MsgItem[] = [
{ title: '采购审批通过', content: '您提交的企业采购单已审批通过,预计 2 日内发货', time: '10:36', read: false },
{ title: '开票通知', content: '订单尾号 8821 的电子发票已开具,请查收邮箱', time: '09:48', read: false },
{ title: '补货提醒', content: '您常购的 A4 纸库存不足一箱,建议及时补货', time: '昨天', read: false },
{ title: '物流更新', content: '您的文具整箱已从广州仓发出,预计后天送达', time: '昨天', read: true },
{ title: '企业专属价', content: '本月企业会员价已更新,打印机耗材直降 15%', time: '前天', read: true },
{ title: '对账单生成', content: '8 月采购对账单已生成,可在企业中心下载', time: '前天', read: true },
{ title: '新品上架', content: '进口中性笔新款到货,0.5mm 针管头书写顺滑', time: '周三', read: true },
{ title: '直播预告', content: '明晚 19:00 办公焕新专场直播,整点抽打印机', time: '周三', read: true },
{ title: '优惠券到账', content: '企业采购满 1000 减 120 券已发放,30 天有效', time: '周四', read: true },
{ title: '评价有礼', content: '评价采购的办公椅可领 30 元无门槛券', time: '上周', read: true },
{ title: '以旧换新', content: '旧打印机折价回收,最高抵 800 元', time: '上周', read: true },
{ title: '安全提醒', content: '谨防冒充供应商的变更收款账号诈骗', time: '上周', read: true }
]
private tabs: TabInfo[] = [
{ name: '文具集市', icon: '✏️' },
{ name: '书写工具', icon: '🖊️' },
{ name: '办公纸品', icon: '📄' },
{ name: '桌面收纳', icon: '🗄️' },
{ name: '企业采购', icon: '🏢' },
{ name: '消息', icon: '✉️' },
{ name: '我的', icon: '👤' }
]
private cats: string[] = ['中性笔', '钢笔', '荧光笔', '铅笔', '马克笔', '白板笔']
private uses: string[] = ['学生刷题', '办公签字', '教师批改', '绘画手账']
private specs: string[] = ['0.38mm 黑', '0.5mm 黑', '0.5mm 蓝', '12 色混装']
private invTypes: string[] = ['电子普票', '纸质普票', '增值税专票']
private titles: string[] = ['个人抬头', '企业抬头', '单位抬头']
private items: GoodsItem[] = [
{ name: '百乐P500中性笔0.5', price: 9, desc: '直液式 大容量顺滑', score: 96, num: 5, tag: '刷题神器', hot: true },
{ name: '三菱UM-151中性笔', price: 8, desc: '低粘度油墨 快干', score: 95, num: 5, tag: '经典款', hot: false },
{ name: '晨光优品按动笔', price: 3, desc: '国货性价比 顺滑不断墨', score: 93, num: 5, tag: '性价比', hot: true },
{ name: '百乐78G+钢笔', price: 68, desc: 'F尖 磨砂手感 练字', score: 95, num: 3, tag: '入门钢笔', hot: false },
{ name: '写乐四季织钢笔', price: 328, desc: '透明杆 15度尖', score: 96, num: 3, tag: '进阶款', hot: false },
{ name: '凌美狩猎者钢笔', price: 79, desc: '德系经典 ABS笔杆', score: 94, num: 3, tag: '德系', hot: true },
{ name: '斑马JJ15复古色', price: 7, desc: '复古五色 手账配色', score: 94, num: 5, tag: '手账', hot: false },
{ name: '无印双头荧光笔', price: 12, desc: '不透纸 温和色系', score: 93, num: 5, tag: '划重点', hot: false },
{ name: '施德楼马克笔套装', price: 128, desc: '专业设计 24色', score: 95, num: 5, tag: '设计师', hot: false },
{ name: '辉柏嘉水彩铅笔', price: 89, desc: '36色 水溶晕染', score: 94, num: 5, tag: '绘画', hot: false },
{ name: '得力A4复印纸整箱', price: 118, desc: '70g 500张×8包', score: 95, num: 70, tag: '办公刚需', hot: true },
{ name: '晨光笔记本B5本', price: 6, desc: '康奈尔内页 加厚', score: 93, num: 5, tag: '学生党', hot: false },
{ name: '国誉Campus笔记本', price: 15, desc: '东大脑训练格', score: 95, num: 5, tag: '进口', hot: false },
{ name: '得力订书机大号', price: 25, desc: '可订50页 省力', score: 92, num: 5, tag: '办公', hot: false },
{ name: '得力文件夹加厚', price: 4, desc: 'A4双强夹 透明', score: 91, num: 5, tag: '归档', hot: false },
{ name: '桌面文件收纳架', price: 45, desc: '三层分格 竖立收纳', score: 93, num: 5, tag: '收纳', hot: true },
{ name: '透明抽屉收纳盒', price: 35, desc: '五层小抽屉', score: 92, num: 5, tag: '桌面', hot: false },
{ name: '便签纸组合装', price: 16, desc: '76×76 四色各一本', score: 91, num: 5, tag: '便签', hot: false },
{ name: '白板磁吸大号', price: 129, desc: '90×120cm 免打孔', score: 94, num: 5, tag: '会议室', hot: false },
{ name: '人体工学办公椅', price: 899, desc: '腰托可调 网布透气', score: 95, num: 5, tag: '久坐党', hot: true },
{ name: '升降桌电动款', price: 1599, desc: '记忆高度 双电机', score: 96, num: 5, tag: '站坐交替', hot: false },
{ name: '桌面理线器磁吸', price: 19, desc: '告别线材凌乱', score: 90, num: 5, tag: '神器', hot: false },
{ name: '洞洞板桌面置物', price: 59, desc: '自由组合 收纳上墙', score: 92, num: 5, tag: '改造', hot: false },
{ name: '墨囊补给装100枚', price: 22, desc: '通用型 黑蓝红', score: 93, num: 5, tag: '耗材', hot: false }
]
private pens: GoodsItem[] = [
{ name: '百乐P500直液笔', price: 9, desc: '针管头 视觉笔尖', score: 96, num: 5, tag: '笔尖0.5mm', hot: true },
{ name: '三菱UM-100', price: 5, desc: '入门经典 圆润书写', score: 94, num: 10, tag: '笔尖0.5mm', hot: false },
{ name: '三菱SN-220速干', price: 8, desc: '0.4秒速干 左手党', score: 95, num: 10, tag: '笔尖0.4mm', hot: true },
{ name: '斑马JJ77防逆流', price: 9, desc: '高密度颜料 速干', score: 94, num: 10, tag: '笔尖0.5mm', hot: false },
{ name: '百乐Juice果汁笔', price: 7, desc: '16色可选 果汁感', score: 95, num: 4, tag: '笔尖0.4mm', hot: false },
{ name: '百乐Juice up', price: 15, desc: '金属笔尖 精细', score: 96, num: 4, tag: '笔尖0.3mm', hot: false },
{ name: '晨光K35按动', price: 2, desc: '国民按动 随手囤', score: 92, num: 5, tag: '笔尖0.5mm', hot: false },
{ name: '得力臻顺滑', price: 2, desc: '大容量 书写千米', score: 91, num: 5, tag: '笔尖0.5mm', hot: false },
{ name: '派通BLN105', price: 12, desc: '低重心 稳定不累', score: 95, num: 5, tag: '笔尖0.5mm', hot: false },
{ name: '无印经典凝胶笔', price: 10, desc: '简约设计 顺滑', score: 93, num: 5, tag: '笔尖0.5mm', hot: false },
{ name: '凌美2000钢笔', price: 1180, desc: '包豪斯设计 拉丝杆', score: 97, num: 2, tag: '笔尖EF尖', hot: false },
{ name: '百乐CUSTOM74', price: 788, desc: '14K金尖 透明杆', score: 97, num: 2, tag: '笔尖FM尖', hot: true }
]
private papers: GoodsItem[] = [
{ name: '得力A4 70g整箱', price: 118, desc: '8包×500张 复印印', score: 95, num: 70, tag: '70g', hot: true },
{ name: 'Double A 80g', price: 158, desc: '泰国进口 高白度', score: 96, num: 80, tag: '80g', hot: false },
{ name: '百旺A4复印纸', price: 138, desc: '中性施胶 不卡纸', score: 95, num: 70, tag: '70g', hot: false },
{ name: '晨光A3加宽纸', price: 168, desc: '工程图纸打印', score: 93, num: 70, tag: '70g', hot: false },
{ name: '国誉Campus本B5', price: 15, desc: '横线30行 东大脑', score: 95, num: 26, tag: '26孔', hot: true },
{ name: '国誉淡彩曲奇本', price: 18, desc: '少女心配色 淡彩', score: 94, num: 26, tag: '26孔', hot: false },
{ name: '满乐文高级拍纸', price: 22, desc: '厚实书写纸', score: 93, num: 30, tag: '30孔', hot: false },
{ name: '康奈尔笔记本', price: 12, desc: '笔记法分区 记忆', score: 92, num: 26, tag: '26孔', hot: false },
{ name: '牛皮纸档案袋', price: 1, desc: '加厚5系 档案', score: 90, num: 5, tag: '档案', hot: false },
{ name: '复写联单三联', price: 8, desc: '无碳复写 一式三份', score: 91, num: 5, tag: '联单', hot: false }
]
private desks: GoodsItem[] = [
{ name: '三层桌面收纳架', price: 45, desc: 'A4分格 侧挂笔筒', score: 93, num: 5, tag: '收纳', hot: true },
{ name: '亚克力抽屉柜', price: 69, desc: '透明可视 五层', score: 94, num: 5, tag: '透明', hot: false },
{ name: '洞洞板置物套装', price: 59, desc: '桌面小森林 铁艺', score: 92, num: 5, tag: '上墙', hot: false },
{ name: '旋转笔筒大容量', price: 29, desc: '360度转动 分格', score: 93, num: 5, tag: '笔筒', hot: false },
{ name: '文件框立式三层', price: 39, desc: '竖放文件 一目了然', score: 92, num: 5, tag: '文件', hot: true },
{ name: '桌面增高架竹木', price: 79, desc: '抬高视线下收纳', score: 94, num: 5, tag: '增高架', hot: false },
{ name: '理线夹磁吸贴', price: 19, desc: '告别线材缠绕', score: 90, num: 5, tag: '理线', hot: false },
{ name: '白板笔磁性套装', price: 25, desc: '可擦无味 四色', score: 92, num: 5, tag: '白板', hot: false },
{ name: '桌面小日历翻页', price: 15, desc: '台历 可记事', score: 91, num: 5, tag: '台历', hot: false },
{ name: '护眼台灯国AA级', price: 159, desc: '无频闪 自动调光', score: 95, num: 5, tag: '护眼', hot: false }
]
private corps: GoodsItem[] = [
{ name: '企业中性笔定制', price: 2, desc: 'LOGO定制 千支起印', score: 95, num: 5, tag: '定制', hot: true },
{ name: 'A4纸企业直采价', price: 108, desc: '月结账期 免运费', score: 96, num: 70, tag: '直采', hot: false },
{ name: '办公椅批量采购', price: 799, desc: '10把起批发价', score: 95, num: 5, tag: '批发', hot: false },
{ name: '打印机租赁套餐', price: 199, desc: '月付含耗材 上门', score: 94, num: 5, tag: '租赁', hot: true },
{ name: '硒鼓墨盒集采', price: 129, desc: '原装耗材 直降15%', score: 94, num: 5, tag: '耗材', hot: false },
{ name: '绿植租摆服务', price: 299, desc: '月租含养护', score: 92, num: 5, tag: '绿植', hot: false },
{ name: '员工福利礼盒', price: 99, desc: '三档可选 开票', score: 93, num: 5, tag: '福利', hot: false },
{ name: '会议室白板定制', price: 399, desc: '尺寸定制 安装', score: 93, num: 5, tag: '定制', hot: false },
{ name: '碎纸机办公级', price: 599, desc: '4级保密 批量', score: 94, num: 5, tag: '保密', hot: false },
{ name: '考勤机人脸款', price: 899, desc: '企业版 云后台', score: 95, num: 5, tag: '考勤', hot: false }
]
selName(): string {
if (this.selectedItem === null) {
return ''
}
return this.selectedItem.name
}
selDesc(): string {
if (this.selectedItem === null) {
return ''
}
return this.selectedItem.desc
}
selTag(): string {
if (this.selectedItem === null) {
return ''
}
return this.selectedItem.tag
}
selPrice(): number {
if (this.selectedItem === null) {
return 0
}
return this.selectedItem.price
}
selScore(): number {
if (this.selectedItem === null) {
return 0
}
return this.selectedItem.score
}
maxOfNum(list: GoodsItem[]): number {
let m: number = 0
for (let i = 0; i < list.length; i++) {
if (list[i].num > m) {
m = list[i].num
}
}
return m
}
unreadCount(): number {
let c: number = 0
for (let i = 0; i < this.msgs.length; i++) {
if (!this.msgs[i].read) {
c++
}
}
return c
}
@Builder buildHeader() {
Column() {
Row() {
Column() {
Text('京东办公文具馆').fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold)
Row() {
Text('🏢').fontSize(11)
Text('企业采购 · 对公转账 · 增值税开票').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ left: 4 })
}
.margin({ top: 4 })
.alignItems(VerticalAlign.Center)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Column() {
Text('📄').fontSize(18)
Text('开票').fontSize(9).fontColor('rgba(255,255,255,0.8)').margin({ top: 2 })
}
.width(44)
.onClick(() => {
this.selInvType = 0
this.selTitle = 0
this.showInvoice = true
})
Column() {
Text('✉️').fontSize(18)
if (this.unreadCount() > 0) {
Text(this.unreadCount().toString())
.fontSize(8)
.fontColor(Color.White)
.backgroundColor('#FF3B30')
.borderRadius(7)
.padding({ left: 4, right: 4 })
.position({ x: 12, y: -4 })
.scale({ x: 1.12, y: 1.12 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.width(44)
.onClick(() => {
this.currentTab = 5
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 12, bottom: 12 })
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('开学季 · 文具 5 折起').fontSize(11).fontColor('#E3F2FD')
Text('企业会员再享折上 95 折').fontSize(10).fontColor('rgba(255,255,255,0.75)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
Column() {
Text('领券').fontSize(11).fontColor('#283593').backgroundColor('#FFFFFF').borderRadius(12).padding({ left: 12, right: 12, top: 4, bottom: 4 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
.margin({ left: 12 })
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 12 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.backgroundColor('#283593')
}
@Builder buildTab1() {
Column() {
Row() {
Column() {
Text('📚').fontSize(28)
Column() {
Text('开学焕新季').fontSize(15).fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('文具 5 折 · 笔记本买二送一').fontSize(10).fontColor('rgba(255,255,255,0.8)').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
}
.layoutWeight(1)
Column() {
Text('368万+').fontSize(15).fontColor('#FF7043').fontWeight(FontWeight.Bold)
Text('师生已选购').fontSize(9).fontColor('rgba(255,255,255,0.7)').margin({ top: 3 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor('#3949AB')
.borderRadius(14)
.padding(16)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(VerticalAlign.Center)
Row() {
Text('文具精选').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('次日达 · 破损包退'.slice(0, 10)).fontSize(10).fontColor('#9AA0B5').margin({ left: 8 })
}
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
ForEach(this.items, (g: GoodsItem, i: number) => {
Row() {
Column() {
Text(i % 4 === 0 ? '✏️' : (i % 4 === 1 ? '🖊️' : (i % 4 === 2 ? '📄' : '🗄️'))).fontSize(26)
}
.width(72)
.height(72)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#E3F2FD')
.justifyContent(FlexAlign.Center)
Column() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Row() {
Text(g.tag).fontSize(9).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 })
if (g.hot) {
Text('回购率高'.slice(0, 4)).fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.06, y: 1.06 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.margin({ top: 5 })
.alignItems(VerticalAlign.Center)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(6).backgroundColor('#F0F0F5').borderRadius(3)
Column().width(g.score + '%').height(6).backgroundColor('#FF7043').borderRadius(3)
}
.width('100%')
.margin({ top: 6 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(16).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('批量采购').fontSize(10).fontColor(Color.White)
}
.backgroundColor('#283593')
.borderRadius(13)
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.margin({ top: 8 })
.onClick(() => {
this.selectedItem = g
this.bulkNum = 100
this.showBulk = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ left: 12, right: 12, bottom: 10 })
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab2() {
Column() {
Text('笔尖规格柱状图(mm×10)').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
Column() {
Row({ space: 10 }) {
ForEach(this.pens, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height(g.num / this.maxOfNum(this.pens) * 100)
.backgroundColor('#283593')
.borderRadius(4)
Text(g.tag.slice(2, 7)).fontSize(7).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12 })
.alignItems(HorizontalAlign.Start)
Text('书写工具双列精选').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(this.pens, (g: GoodsItem, i: number) => {
Column() {
Row() {
Column() {
Text('🖊️').fontSize(22)
}
.width(44)
.height(44)
.borderRadius(22)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#FFF3E0')
.justifyContent(FlexAlign.Center)
Column() {
Text(g.tag).fontSize(9).fontColor('#283593').fontWeight(FontWeight.Bold)
Text('好评 ' + g.score + '%').fontSize(9).fontColor('#8A8FA3').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
Text(g.name).fontSize(12).fontColor('#212121').fontWeight(FontWeight.Medium).margin({ top: 8 }).maxLines(1)
Text(g.desc).fontSize(9).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Row() {
Text('¥' + g.price).fontSize(14).fontColor('#D84315').fontWeight(FontWeight.Bold)
if (g.hot) {
Text('编辑推荐').fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}
.width('100%')
.margin({ top: 6 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
}
.width('48%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab3() {
Column() {
Column() {
Text('纸张克重分布柱状图(g)').fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Bold)
Row({ space: 10 }) {
ForEach(this.papers, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height(g.num / this.maxOfNum(this.papers) * 100)
.backgroundColor('#FFAB91')
.borderRadius(4)
Text(g.num.toString()).fontSize(8).fontColor('rgba(255,255,255,0.85)').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.margin({ top: 12 })
}
.width('100%')
.backgroundColor('#1A237E')
.borderRadius(14)
.padding(14)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(HorizontalAlign.Start)
Text('口碑排行 · 复购进度').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Column() {
ForEach(this.papers, (g: GoodsItem, i: number) => {
Row() {
Text((i + 1).toString()).fontSize(12).fontColor(Color.White).backgroundColor(i < 3 ? '#FF7043' : '#9AA0B5').borderRadius(10).width(20).height(20).textAlign(TextAlign.Center)
Column() {
Text(g.name).fontSize(12).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(7).backgroundColor('#F0F0F5').borderRadius(4)
Column().width(g.score + '%').height(7).backgroundColor('#283593').borderRadius(4)
}
.width('100%')
.margin({ top: 5 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold)
if (g.hot) {
Text('🔥 囤货王').fontSize(8).fontColor('#D84315').margin({ top: 3 })
.scale({ x: 1.1, y: 1.1 })
.animation({ duration: 600, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
.margin({ bottom: 12 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12 })
Text('本册速览 · 横滑'.slice(0, 8)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Scroll() {
Row({ space: 10 }) {
ForEach(this.papers, (g: GoodsItem, i: number) => {
Column() {
Column() {
Text('📄').fontSize(28)
}
.width(76)
.height(76)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E3F2FD' : '#E8EAF6')
.justifyContent(FlexAlign.Center)
Text(g.name).fontSize(11).fontColor('#212121').margin({ top: 8 }).maxLines(1)
Text('¥' + g.price).fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold).margin({ top: 4 })
}
.width(112)
.backgroundColor(Color.White)
.borderRadius(12)
.padding(10)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
.width('100%')
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab4() {
Column() {
Text('桌面收纳专区').fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 14, bottom: 8 })
Column() {
ForEach(this.desks, (g: GoodsItem, i: number) => {
Row() {
Column()
.width(4)
.height(48)
.backgroundColor(i % 4 === 0 ? '#283593' : (i % 4 === 1 ? '#FF7043' : (i % 4 === 2 ? '#00897B' : '#5C6BC0')))
.borderRadius(2)
Column() {
Row() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.tag).fontSize(9).fontColor('#00897B').backgroundColor('#E0F2F1').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
if (g.hot) {
Text('热销').fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(VerticalAlign.Center)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Text('好评率 ' + g.score + '%'.slice(0, 1) + ')').fontSize(9).fontColor('#9AA0B5').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price).fontSize(15).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('查看详情'.slice(0, 4)).fontSize(9).fontColor('#283593')
}
.borderRadius(11)
.border({ width: 1, color: '#283593' })
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.margin({ top: 6 })
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.selectedItem = g
this.showDetail = true
})
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
Text('好评分段柱状图'.slice(0, 7)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 10, bottom: 8 })
Column() {
Row({ space: 12 }) {
ForEach(this.desks, (g: GoodsItem) => {
Column() {
Column()
.width(16)
.height((g.score - 85) / 10 * 90)
.backgroundColor('#00897B')
.borderRadius(4)
Text(g.score.toString()).fontSize(8).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
}
.alignItems(HorizontalAlign.Start)
}, (g: GoodsItem) => g.name)
}
.width('100%')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(14)
.margin({ left: 12, right: 12, top: 4 })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab5() {
Column() {
Column() {
Row() {
Column() {
Text('企业采购中心').fontSize(17).fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('对公转账 · 月结账期 · 专属客户经理'.slice(0, 15)).fontSize(10).fontColor('rgba(255,255,255,0.75)').margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('🏢').fontSize(32)
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
}
.width('100%')
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('86%').fontSize(22).fontColor('#FFAB91').fontWeight(FontWeight.Bold)
Text('综合好评').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text('3200+').fontSize(22).fontColor('#FFF59D').fontWeight(FontWeight.Bold).margin({ top: 2 })
.scale({ x: 1.04, y: 1.04 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
Text('服务企业').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text('T+7').fontSize(22).fontColor('#80CBC4').fontWeight(FontWeight.Bold).margin({ top: 2 })
Text('最快账期').fontSize(10).fontColor('rgba(255,255,255,0.7)').margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.margin({ top: 16 })
.alignItems(VerticalAlign.Top)
}
.width('100%')
.backgroundColor('#283593')
.borderRadius(14)
.padding(18)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(HorizontalAlign.Start)
Text('集采服务项目'.slice(0, 6)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 16, top: 16, bottom: 8 })
Column() {
ForEach(this.corps, (g: GoodsItem, i: number) => {
Row() {
Column() {
Text(i % 5 === 0 ? '🏢' : (i % 5 === 1 ? '🧾' : (i % 5 === 2 ? '🖨️' : (i % 5 === 3 ? '🪴' : '🪑')))).fontSize(24)
}
.width(52)
.height(52)
.borderRadius(12)
.backgroundColor(i % 2 === 0 ? '#E8EAF6' : '#E0F2F1')
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(g.name).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(g.tag).fontSize(9).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
if (g.hot) {
Text('客户点赞'.slice(0, 4)).fontSize(9).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 6 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
}
.alignItems(VerticalAlign.Center)
Text(g.desc).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 }).maxLines(1)
Text('好评率 ' + g.score + '% · 已合作 ' + g.num * 36 + ' 家').fontSize(9).fontColor('#9AA0B5').margin({ top: 3 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
Column() {
Text('¥' + g.price + ' 起').fontSize(13).fontColor('#D84315').fontWeight(FontWeight.Bold)
Column() {
Text('批量采购'.slice(0, 4)).fontSize(10).fontColor(Color.White)
}
.backgroundColor('#283593')
.borderRadius(13)
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.margin({ top: 6 })
.onClick(() => {
this.selectedItem = g
this.bulkNum = 100
this.showBulk = true
})
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ bottom: 10 })
.alignItems(VerticalAlign.Center)
}, (g: GoodsItem) => g.name)
}
.width('100%')
.padding({ left: 12, right: 12 })
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab6() {
Column() {
Row() {
Text('消息中心').fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('未读 ' + this.unreadCount() + ' 条').fontSize(11).fontColor('#FF3B30').margin({ left: 8 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
.width('100%')
.padding({ left: 16, right: 16, top: 14, bottom: 10 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
ForEach(this.msgs, (m: MsgItem, i: number) => {
Row() {
Column() {
Text(m.read ? '📭' : '📎').fontSize(20)
if (!m.read) {
Column()
.width(8)
.height(8)
.borderRadius(4)
.backgroundColor('#FF3B30')
.position({ x: 30, y: -2 })
.scale({ x: 1.2, y: 1.2 })
.animation({ duration: 600, iterations: -1, curve: Curve.EaseInOut })
}
}
.width(44)
Column() {
Row() {
Text(m.title).fontSize(13).fontColor(m.read ? '#5C6280' : '#212121').fontWeight(m.read ? FontWeight.Normal : FontWeight.Bold).maxLines(1)
Text(m.time).fontSize(9).fontColor('#9AA0B5').margin({ left: 6 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Text(m.content).fontSize(11).fontColor('#8A8FA3').margin({ top: 4 }).maxLines(2)
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 8 })
Column() {
Text('删除').fontSize(10).fontColor('#9AA0B5')
}
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.border({ width: 1, color: '#E5E5EC' })
.borderRadius(10)
.onClick(() => {
this.selectedItem = null
this.deleteIndex = i
this.showDelete = true
})
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(12)
.margin({ left: 12, right: 12, bottom: 10 })
.alignItems(VerticalAlign.Center)
}, (m: MsgItem) => m.title + m.time)
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildTab7() {
Column() {
Row() {
Column() {
Text('🏢').fontSize(34)
}
.width(64)
.height(64)
.borderRadius(32)
.backgroundColor('#E8EAF6')
.justifyContent(FlexAlign.Center)
Column() {
Text('行政小管家').fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Row() {
Text('企业会员').fontSize(9).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 })
Text('累计采购 ¥28.6万'.slice(0, 12)).fontSize(10).fontColor('#9AA0B5').margin({ left: 8 })
}
.margin({ top: 6 })
.alignItems(VerticalAlign.Center)
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
Text('›').fontSize(20).fontColor('#C5C9D9')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(14)
.padding(16)
.margin({ left: 12, right: 12, top: 12 })
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('4').fontSize(18).fontColor('#283593').fontWeight(FontWeight.Bold)
Text('待审批').fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
}
.layoutWeight(1)
Column() {
Text('26').fontSize(18).fontColor('#FF7043').fontWeight(FontWeight.Bold)
Text('采购单').fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
}
.layoutWeight(1)
Column() {
Text('8').fontSize(18).fontColor('#00897B').fontWeight(FontWeight.Bold)
Text('待开票').fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
}
.layoutWeight(1)
Column() {
Text('2').fontSize(18).fontColor('#D84315').fontWeight(FontWeight.Bold)
Text('优惠券').fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
}
.layoutWeight(1)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(14)
.padding({ top: 14, bottom: 14 })
.margin({ left: 12, right: 12, top: 12 })
Column() {
Row() {
Text('🧾').fontSize(18)
Text('采购订单').fontSize(13).fontColor('#212121').margin({ left: 10 })
Text('全部 26 ›'.slice(0, 9)).fontSize(11).fontColor('#9AA0B5')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Row() {
Text('📄').fontSize(18)
Text('发票管理').fontSize(13).fontColor('#212121').margin({ left: 10 })
Text('8 张待开票 ›'.slice(0, 9)).fontSize(11).fontColor('#D84315')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Row() {
Text('👥').fontSize(18)
Text('员工领用台账').fontSize(13).fontColor('#212121').margin({ left: 10 })
Text('本月 86 件 ›'.slice(0, 9)).fontSize(11).fontColor('#9AA0B5')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Row() {
Text('📊').fontSize(18)
Text('采购月度报表'.slice(0, 6)).fontSize(13).fontColor('#212121').margin({ left: 10 })
Text('8 月已生成 ›'.slice(0, 8)).fontSize(11).fontColor('#9AA0B5')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Row() {
Text('⚙️').fontSize(18)
Text('设置').fontSize(13).fontColor('#212121').margin({ left: 10 })
Text('›').fontSize(14).fontColor('#C5C9D9')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(14)
.padding({ left: 16, right: 16 })
.margin({ left: 12, right: 12, top: 12 })
Column() {
Text('闲置办公用品转让给需要的团队'.slice(0, 14)).fontSize(12).fontColor('#8A8FA3')
Column() {
Text('发布闲置文具').fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.width(180)
.height(40)
.backgroundColor('#283593')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ top: 12 })
.scale({ x: 1.04, y: 1.04 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
.onClick(() => {
this.selCat = 0
this.selUse = 0
this.showPublish = true
})
}
.width('100%')
.padding({ top: 20, bottom: 20 })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding({ bottom: 12 })
}
@Builder buildBottom() {
Row() {
ForEach(this.tabs, (t: TabInfo, i: number) => {
Column() {
Text(t.icon).fontSize(18)
Text(t.name).fontSize(9).fontColor(this.currentTab === i ? '#283593' : '#9AA0B5').margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 6, bottom: 6 })
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentTab = i
})
}, (t: TabInfo) => t.name)
}
.width('100%')
.backgroundColor(Color.White)
.border({ width: 1, color: '#EDEDF2' })
}
@Builder publishModal() {
Column() {
Column() {
Row() {
Text('发布闲置文具').fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('✕').fontSize(16).fontColor('#9AA0B5')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Column() {
Text('物品描述').fontSize(12).fontColor('#5C6280')
TextInput({ placeholder: '如:凌美狩猎者钢笔 EF尖 九成新' })
.height(40)
.backgroundColor('#F5F6FA')
.borderRadius(8)
.margin({ top: 6 })
.fontSize(12)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 16 })
Column() {
Text('物品品类').fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.cats, (c: string, i: number) => {
Text(c).fontSize(11).fontColor(this.selCat === i ? Color.White : '#5C6280')
.backgroundColor(this.selCat === i ? '#283593' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selCat = i
})
}, (c: string) => c)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Column() {
Text('适用场景'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.uses, (u: string, i: number) => {
Text(u).fontSize(11).fontColor(this.selUse === i ? Color.White : '#5C6280')
.backgroundColor(this.selUse === i ? '#00897B' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selUse = i
})
}, (u: string) => u)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Row() {
Column() {
Text('暂不发布'.slice(0, 4)).fontSize(13).fontColor('#5C6280')
}
.layoutWeight(1)
.height(40)
.backgroundColor('#F5F6FA')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showPublish = false
})
Column() {
Text('确认发布'.slice(0, 4)).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.layoutWeight(1.6)
.height(40)
.backgroundColor('#283593')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.scale({ x: 1.02, y: 1.02 })
.animation({ duration: 800, iterations: -1, curve: Curve.EaseInOut })
.onClick(() => {
this.showPublish = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('92%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(16)
.constraintSize({ maxHeight: '80%' })
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showPublish = false
})
}
@Builder bulkModal() {
Column() {
Column() {
Row() {
Column() {
Text('批量采购询价'.slice(0, 6)).fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('量大从优 · 免费开票 · 月结可选'.slice(0, 14)).fontSize(10).fontColor('#283593').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
Text('✕').fontSize(16).fontColor('#9AA0B5')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Column() {
Text(this.selName()).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).maxLines(1)
Text(this.selDesc()).fontSize(10).fontColor('#8A8FA3').margin({ top: 3 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Column() {
Text('规格选择'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.specs, (s: string, i: number) => {
Text(s).fontSize(11).fontColor(this.selSpec === i ? Color.White : '#5C6280')
.backgroundColor(this.selSpec === i ? '#283593' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selSpec = i
})
}, (s: string) => s)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Row() {
Column() {
Text('采购数量(件)'.slice(0, 7)).fontSize(11).fontColor('#5C6280')
Row() {
Text('-').fontSize(15).fontColor('#283593').width(28).height(28).textAlign(TextAlign.Center).backgroundColor('#F5F6FA').borderRadius(8)
.onClick(() => {
if (this.bulkNum > 10) {
this.bulkNum = this.bulkNum - 10
}
})
Text(this.bulkNum.toString()).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Medium).width(60).textAlign(TextAlign.Center)
Text('+').fontSize(15).fontColor('#283593').width(28).height(28).textAlign(TextAlign.Center).backgroundColor('#F5F6FA').borderRadius(8)
.onClick(() => {
if (this.bulkNum < 5000) {
this.bulkNum = this.bulkNum + 10
}
})
}
.margin({ top: 6 })
.alignItems(VerticalAlign.Center)
}
.alignItems(HorizontalAlign.Start)
Column() {
Text('预估总价'.slice(0, 4)).fontSize(11).fontColor('#5C6280')
Text('¥' + (this.selPrice() * this.bulkNum)).fontSize(22).fontColor('#D84315').fontWeight(FontWeight.Bold).margin({ top: 6 })
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text('已享批量 92 折'.slice(0, 9)).fontSize(9).fontColor('#2E7D32').margin({ top: 3 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 16 })
.alignItems(VerticalAlign.Top)
Row() {
Column() {
Text('仅询价'.slice(0, 3)).fontSize(11).fontColor('#8A8FA3')
}
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.border({ width: 1, color: '#E5E5EC' })
.borderRadius(12)
.onClick(() => {
this.showBulk = false
})
Column() {
Text('提交采购单'.slice(0, 5)).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.height(40)
.backgroundColor('#283593')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
this.showBulk = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(16)
.constraintSize({ maxHeight: '80%' })
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.End)
.onClick(() => {
this.showBulk = false
})
}
@Builder invoiceModal() {
Column() {
Column() {
Row() {
Column() {
Text('开票申请').fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold)
Text('电子发票 30 秒极速开具'.slice(0, 12)).fontSize(10).fontColor('#00897B').margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
Text('✕').fontSize(16).fontColor('#9AA0B5')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Column() {
Text('发票类型'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.invTypes, (t: string, i: number) => {
Text(t).fontSize(11).fontColor(this.selInvType === i ? Color.White : '#5C6280')
.backgroundColor(this.selInvType === i ? '#283593' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selInvType = i
})
}, (t: string) => t)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 16 })
Column() {
Text('发票抬头'.slice(0, 4)).fontSize(12).fontColor('#5C6280')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.titles, (t: string, i: number) => {
Text(t).fontSize(11).fontColor(this.selTitle === i ? Color.White : '#5C6280')
.backgroundColor(this.selTitle === i ? '#00897B' : '#F5F6FA')
.borderRadius(13)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, top: 8 })
.onClick(() => {
this.selTitle = i
})
}, (t: string) => t)
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Column() {
Text('开票金额区间'.slice(0, 6)).fontSize(12).fontColor('#5C6280')
Row({ space: 8 }) {
Text('¥100-500').fontSize(11).fontColor('#5C6280').backgroundColor('#F5F6FA').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 })
Text('¥500-2000').fontSize(11).fontColor('#5C6280').backgroundColor('#F5F6FA').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 })
Text('¥2000+').fontSize(11).fontColor(Color.White).backgroundColor('#D84315').borderRadius(10).padding({ left: 10, right: 10, top: 6, bottom: 6 }).margin({ left: 8 })
.scale({ x: 1.08, y: 1.08 })
.animation({ duration: 650, iterations: -1, curve: Curve.EaseInOut })
}
.margin({ top: 8 })
.alignItems(VerticalAlign.Center)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 14 })
Row() {
Column() {
Text('取消'.slice(0, 2)).fontSize(13).fontColor('#5C6280')
}
.layoutWeight(1)
.height(40)
.backgroundColor('#F5F6FA')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showInvoice = false
})
Column() {
Text('提交开票'.slice(0, 4)).fontSize(13).fontColor(Color.White).fontWeight(FontWeight.Medium)
}
.layoutWeight(1.6)
.height(40)
.backgroundColor('#00897B')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
this.showInvoice = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('92%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(16)
.constraintSize({ maxHeight: '80%' })
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showInvoice = false
})
}
@Builder deleteModal() {
Column() {
Column() {
Text('🗑️').fontSize(38)
.scale({ x: 1.12, y: 1.12 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text('确认删除这条消息?'.slice(0, 11)).fontSize(15).fontColor('#212121').fontWeight(FontWeight.Bold).margin({ top: 12 })
Text('删除后该通知将无法恢复'.slice(0, 12)).fontSize(11).fontColor('#9AA0B5').margin({ top: 6 })
Row() {
Column() {
Text('再想想'.slice(0, 3)).fontSize(13).fontColor('#5C6280')
}
.layoutWeight(1)
.height(40)
.backgroundColor('#F5F6FA')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showDelete = false
})
Column() {
Text('确认删除'.slice(0, 4)).fontSize(13).fontColor(Color.White)
}
.layoutWeight(1)
.height(40)
.backgroundColor('#D84315')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
if (this.deleteIndex >= 0 && this.deleteIndex < this.msgs.length) {
this.msgs.splice(this.deleteIndex, 1)
}
this.deleteIndex = -1
this.showDelete = false
})
}
.width('100%')
.margin({ top: 20 })
.alignItems(VerticalAlign.Center)
}
.width('76%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(20)
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showDelete = false
})
}
@Builder detailModal() {
Column() {
Column() {
Column() {
Column() {
Text('✏️').fontSize(40)
}
.width(96)
.height(96)
.borderRadius(16)
.backgroundColor('#E8EAF6')
.justifyContent(FlexAlign.Center)
Text(this.selName()).fontSize(16).fontColor('#212121').fontWeight(FontWeight.Bold).margin({ top: 12 }).maxLines(2)
Text(this.selDesc()).fontSize(11).fontColor('#8A8FA3').margin({ top: 6 }).maxLines(2)
Row() {
Text(this.selTag()).fontSize(10).fontColor('#283593').backgroundColor('#E8EAF6').borderRadius(4).padding({ left: 6, right: 6, top: 3, bottom: 3 })
Text('好评 ' + this.selScore() + '%').fontSize(10).fontColor('#2E7D32').margin({ left: 8 })
}
.margin({ top: 8 })
.alignItems(VerticalAlign.Center)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('商品参数'.slice(0, 4)).fontSize(13).fontColor('#212121').fontWeight(FontWeight.Bold)
Row() {
Text('发货时效').fontSize(11).fontColor('#8A8FA3')
Text('次日达 · 假一赔十'.slice(0, 9)).fontSize(11).fontColor('#212121').margin({ left: 8 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 10 })
Row() {
Text('售后政策'.slice(0, 4)).fontSize(11).fontColor('#8A8FA3')
Text('7天无理由 · 破损包退'.slice(0, 11)).fontSize(11).fontColor('#212121').margin({ left: 8 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 8 })
Row() {
Text('开票支持'.slice(0, 4)).fontSize(11).fontColor('#8A8FA3')
Text('普票 / 专票均可'.slice(0, 9)).fontSize(11).fontColor('#212121').margin({ left: 8 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 18 })
Row() {
Text('¥' + this.selPrice()).fontSize(22).fontColor('#D84315').fontWeight(FontWeight.Bold)
.scale({ x: 1.05, y: 1.05 })
.animation({ duration: 700, iterations: -1, curve: Curve.EaseInOut })
Text('满99减15'.slice(0, 6)).fontSize(10).fontColor(Color.White).backgroundColor('#FF3B30').borderRadius(4).padding({ left: 5, right: 5, top: 2, bottom: 2 }).margin({ left: 8 })
}
.width('100%')
.margin({ top: 16 })
.alignItems(VerticalAlign.Center)
Row() {
Column() {
Text('申请开票'.slice(0, 4)).fontSize(12).fontColor('#00897B')
}
.layoutWeight(1)
.height(38)
.borderRadius(19)
.border({ width: 1, color: '#00897B' })
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showDetail = false
this.showInvoice = true
})
Column() {
Text('批量采购'.slice(0, 4)).fontSize(12).fontColor(Color.White)
}
.layoutWeight(1)
.height(38)
.backgroundColor('#283593')
.borderRadius(19)
.justifyContent(FlexAlign.Center)
.margin({ left: 10 })
.onClick(() => {
this.showDetail = false
this.showBulk = true
})
}
.width('100%')
.margin({ top: 18 })
.alignItems(VerticalAlign.Center)
}
.width('80%')
.height('100%')
.backgroundColor(Color.White)
.padding(16)
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.End)
.alignItems(HorizontalAlign.End)
.onClick(() => {
this.showDetail = false
})
}
build() {
Stack() {
Column() {
this.buildHeader()
Scroll() {
Column() {
if (this.currentTab === 0) {
this.buildTab1()
} else if (this.currentTab === 1) {
this.buildTab2()
} else if (this.currentTab === 2) {
this.buildTab3()
} else if (this.currentTab === 3) {
this.buildTab4()
} else if (this.currentTab === 4) {
this.buildTab5()
} else if (this.currentTab === 5) {
this.buildTab6()
} else {
this.buildTab7()
}
}
.width('100%')
}
.layoutWeight(1)
.scrollBar(BarState.Off)
.width('100%')
.align(Alignment.Top)
this.buildBottom()
}
.width('100%')
.height('100%')
if (this.showPublish) {
this.publishModal()
}
if (this.showBulk) {
this.bulkModal()
}
if (this.showInvoice) {
this.invoiceModal()
}
if (this.showDelete) {
this.deleteModal()
}
if (this.showDetail) {
this.detailModal()
}
}
.width('100%')
.height('100%')
.backgroundColor('#F4F6FB')
}
}
总结

本文深入解析了一个基于HarmonyOS声明式UI的办公文具电商应用的完整源码实现。从数据模型定义到多标签页构建,从笔尖规格可视化到企业采购中心,从批量询价弹窗到智能开票系统,该应用涵盖了办公文具B端电商开发中的全部核心技术点。应用通过@State状态管理实现了UI的响应式更新,通过@Builder方法拆分实现了视图逻辑的模块化组织,通过interface接口保证了数据的类型安全,在架构层面与前两个应用保持了一致的设计哲学。
在B端业务功能层面,应用展现了极高的专业完成度。批量采购询价弹窗的步进10设计直接反映了企业采购的批量特征,92折优惠信息的绿色展示有效促进了转化。开票申请弹窗覆盖了电子普票、纸质普票和增值税专票三种发票类型,以及个人、企业、单位三种抬头选择,双色选择器设计通过颜色差异帮助用户区分不同维度的选择。企业采购中心的数据面板使用三种不同颜色的数据指标(好评橙、企业黄、账期青),使得关键业务数据一目了然。这些B端专属功能的设计充分考虑了企业用户的实际采购流程和开票需求。
在技术实现层面,桌面收纳页面的好评分段柱状图采用了(g.score - 85) / 10 * 90的分段放大公式,这是数据可视化中的经典技术——当数据集中在狭窄范围内时,通过减去基线值再进行比例放大,使得微小差异在视觉上更加明显。应用中大量使用的slice方法进行字符串截取,既用于控制文本长度适配不同屏幕宽度,也用于从复合标签中提取数值部分(如从"笔尖0.5mm"中提取"0.5mm"),展示了ArkTS在字符串处理方面的灵活性。整体而言,该应用的源码为HarmonyOS平台上的办公文具B端电商开发提供了一个功能完整、交互丰富、专业度高的实践参考,与前两个应用共同构成了垂直电商场景的完整技术体系。
更多推荐



所有评论(0)