HarmonyOS 6效果实现:vgFloatY函数实现轻盈的浮动效果,如同玻璃在微风中轻轻摇曳
基于HarmonyOS API 24的ArkTS彩绘玻璃艺术工坊深度解析——HarmonyOS 6.1.1光与色彩艺术UI实战
在HarmonyOS 6.1.1的技术舞台上,光与色彩的艺术正在以全新的方式绽放。本文将深入剖析一款彩绘玻璃艺术工坊应用的技术实现,从彩色玻璃光效到多色映射系统,从课程培训体系到作品商城,全方位展示HarmonyOS ArkTS API 24在艺术类应用中的创新表达。
靛蓝的深邃、琥珀金的璀璨、宝石红的艳丽,当这些彩绘玻璃的经典色彩在移动屏幕上流淌,光与影的艺术便有了全新的载体。这款彩绘玻璃工坊应用以前所未有的视觉表现力,将千年玻璃艺术带入了鸿蒙生态。
本文将从彩色主题系统、多色映射机制、光效动画引擎、数据模型设计、课程培训体系、六大Tab功能实现等多个维度,系统拆解这款彩绘玻璃艺术应用的技术内核,为HarmonyOS开发者提供艺术品类应用的设计范式与开发参考。
引言

随着HarmonyOS 6.1.1的正式发布,ArkTS声明式UI框架迎来了新的发展里程碑,为各类创意应用的开发提供了更加丰富的技术手段。这款彩绘玻璃艺术工坊应用正是基于HarmonyOS ArkTS API 24构建的一款艺术品类应用,它以"光与色彩的艺术"为核心理念,将传统彩绘玻璃工艺与现代移动技术完美融合。
应用整体采用"VG"(Vitrail Glass)前缀的命名规范,以靛蓝(#4338CA)为主色调,琥珀金(#D97706)为强调色,搭配宝石红(#BE123C)、翡翠绿(#047857)、钴蓝(#1D4ED8)等多种彩色玻璃专用色,构建出绚丽而不失雅致的视觉体系。在淡紫色(#F1EFF7)的背景衬托下,各色玻璃如宝石般熠熠生辉,完美诠释了彩绘玻璃艺术的精髓。
从业务架构来看,这是一款集玻璃材料销售、成品作品展示、工具耗材供应、课程培训教学于一体的综合性彩绘玻璃艺术平台。六大核心Tab页签——首页、玻璃、作品、工具、课程、我的——构建了完整的用户旅程,从选购玻璃材料到欣赏成品作品,从购买专业工具到参加系统课程,全方位满足彩绘玻璃爱好者从入门到进阶的全部需求。
技术实现方面,这款应用是四款应用中代码量最大、功能最丰富的一款。除了经典的接口定义层、纯函数工具层、组件渲染层三层架构外,还引入了独特的多色映射系统(根据玻璃颜色名称返回对应颜色值、根据作品风格返回对应主题色),以及完整的课程培训体系(入门/进阶/高级/亲子多级别课程)。这些特色功能使得应用在技术深度和业务丰富度上都更上一层楼。
彩色主题系统设计

主题接口与基础配色
interface VgTheme {
bg: string
primary: string
accent: string
card: string
text: string
sub: string
line: string
glow: string
dark: string
}
const VG: VgTheme = {
bg: '#F1EFF7',
primary: '#4338CA',
accent: '#D97706',
card: '#FFFFFF',
text: '#241E3E',
sub: '#837CA8',
line: '#E2DDF0',
glow: '#7C6FF0',
dark: '#241E3E'
}
基础主题系统采用靛蓝为主色、琥珀金为强调色的配色方案。靛蓝(#4338CA)深邃而高贵,象征着彩绘玻璃艺术的典雅气质;琥珀金(#D97706)温暖而璀璨,如同阳光透过彩色玻璃洒下的金色光芒。背景色使用淡紫色(#F1EFF7),既柔和又带有一丝神秘感,为各色玻璃元素提供了完美的展示舞台。
主色与强调色的选择是品牌视觉识别的核心。靛蓝与琥珀金的组合既对比鲜明又和谐统一,在传递艺术感的同时保持了良好的可读性和可用性。
彩色玻璃色板

const VG_RUBY: string = '#BE123C'
const VG_EMERALD: string = '#047857'
const VG_COBALT: string = '#1D4ED8'
const VG_AMBER: string = '#D97706'
除了基础主题色外,应用还定义了四个彩色玻璃专用色常量:宝石红、翡翠绿、钴蓝、琥珀金。这些颜色是彩绘玻璃中最经典、最具代表性的颜色,它们不仅用于玻璃产品的视觉展示,还广泛应用于各种装饰元素和强调效果中。
在彩绘玻璃艺术中,颜色是最重要的表现语言之一。每种颜色的玻璃都有其独特的名称和特性,如钴蓝、红宝石、祖母绿等。应用中保留这些专业色彩名称,既能提升产品的专业感,也能帮助用户建立准确的色彩认知。
Tab导航配置
const VG_TABS: VgTab[] = [
{ name: '首页', icon: '◈' },
{ name: '玻璃', icon: '▩' },
{ name: '作品', icon: '✦' },
{ name: '工具', icon: '⚒' },
{ name: '课程', icon: '✧' },
{ name: '我的', icon: '◉' }
]
与其他三款应用不同,这款彩绘玻璃应用的Tab图标使用了特殊符号而非图片资源。每个符号都与对应Tab的功能有所关联:首页用菱形(◈)象征宝石,玻璃用方格(▩)象征玻璃纹理,作品用星号(✦)象征艺术,工具用锤子(⚒)象征制作,课程用星芒(✧)象征学习,我的用圆环(◉)象征个人中心。这种设计方式既简洁又富有艺术气息。
使用特殊字符作为图标是一种轻量级的图标解决方案。它不需要额外的图片资源,加载速度快,风格统一,且可以通过fontColor属性轻松改变颜色。对于追求简约艺术风格的应用来说,这是一种非常优雅的选择。
数据模型与业务数据

玻璃材料数据
interface VgGlass {
name: string
colorName: string
texture: string
thickness: number
trans: number
price: number
stock: number
sold: number
tag: string
}
const VG_GLASS: VgGlass[] = [
{ name: '钴蓝教堂玻璃', colorName: '钴蓝', texture: '水纹', thickness: 3, trans: 78, price: 42, stock: 120, sold: 356, tag: '热销' },
{ name: '宝石红玻璃', colorName: '宝石红', texture: '锤纹', thickness: 3, trans: 72, price: 45, stock: 86, sold: 312, tag: '热销' },
{ name: '琥珀金玻璃', colorName: '琥珀金', texture: '猫眼', thickness: 4, trans: 66, price: 52, stock: 64, sold: 288, tag: '进口' },
{ name: '翡翠绿玻璃', colorName: '翡翠绿', texture: '麻纹', thickness: 3, trans: 75, price: 38, stock: 150, sold: 274, tag: '经典' },
{ name: '虹彩玻璃', colorName: '虹彩', texture: '镀膜', thickness: 4, trans: 58, price: 68, stock: 40, sold: 245, tag: '进口' },
// ... 更多数据
]
玻璃材料的数据模型设计非常专业,包含颜色名称、纹理、厚度、透光率等核心属性。纹理类型有水纹、锤纹、猫眼、麻纹、镀膜、雪花、水滴、起皱、手吹、镜面等多种,充分展示了彩绘玻璃材料的丰富性。透光率(trans)以百分比表示,是玻璃材料的重要参数之一。
数据包含10款玻璃产品,颜色涵盖钴蓝、宝石红、琥珀金、翡翠绿、虹彩、乳白、紫水晶、海蓝、琥珀、墨黑等多种色彩,价格从35元到76元不等,既有入门级产品也有高端进口材料。
材料类产品的数据模型需要特别关注物理属性。厚度、透光率、纹理这些参数对于彩绘玻璃从业者来说是重要的选购依据,它们直接影响作品的最终效果。专业的数据模型是赢得专业用户信任的基础。
作品数据

interface VgWork {
name: string
style: string
size: string
panels: number
colorCount: number
price: number
sold: number
tag: string
}
const VG_WORKS: VgWork[] = [
{ name: '哥特玫瑰窗', style: '哥特', size: '60×60cm', panels: 12, colorCount: 5, price: 1280, sold: 46, tag: '镇店' },
{ name: '蒂芙尼蜻蜓灯罩', style: '蒂芙尼', size: '40×25cm', panels: 9, colorCount: 4, price: 980, sold: 68, tag: '热销' },
{ name: '现代向日葵挂屏', style: '现代', size: '50×35cm', panels: 8, colorCount: 3, price: 720, sold: 92, tag: '热销' },
{ name: '新艺术鸢尾竖窗', style: '新艺术', size: '35×80cm', panels: 10, colorCount: 4, price: 1180, sold: 55, tag: '精品' },
{ name: '拜占庭穹顶灯', style: '拜占庭', size: 'Ø45cm', panels: 14, colorCount: 6, price: 1580, sold: 33, tag: '精品' },
// ... 更多数据
]
成品作品的数据模型包含风格、尺寸、玻璃片数、颜色数等专业属性。作品风格涵盖哥特、蒂芙尼、现代、新艺术、拜占庭等多种经典流派,展示了彩绘玻璃艺术的多样性。玻璃片数(panels)和颜色数(colorCount)是衡量作品复杂度和工艺价值的重要指标。
作品数据中的"panels"(玻璃片数)是一个非常专业的属性。在彩绘玻璃艺术中,作品的价值往往与玻璃片的数量直接相关——更多的玻璃片意味着更复杂的图案和更高的工艺难度。将这一参数展示给用户,能够有效传递作品的工艺价值。
课程数据

interface VgCourse {
name: string
level: string
lessons: number
hours: number
students: number
price: number
}
const VG_COURSES: VgCourse[] = [
{ name: '彩绘玻璃入门八讲', level: '入门', lessons: 8, hours: 16, students: 326, price: 398 },
{ name: '蒂芙尼灯罩进阶班', level: '进阶', lessons: 12, hours: 30, students: 158, price: 1280 },
{ name: '割刀手法专训课', level: '入门', lessons: 4, hours: 8, students: 288, price: 268 },
{ name: '哥特花窗设计课', level: '高级', lessons: 10, hours: 26, students: 96, price: 1680 },
{ name: '玻璃彩绘着色课', level: '进阶', lessons: 6, hours: 12, students: 204, price: 588 },
{ name: '亲子玻璃体验营', level: '亲子', lessons: 3, hours: 5, students: 412, price: 198 }
]
课程培训是这款应用的特色业务板块。课程数据模型包含级别、课时数、时长、学员数、价格等属性。课程级别分为入门、进阶、高级、亲子四个等级,形成了完整的学习路径。从398元的入门课程到1680元的高级设计课,价格梯度合理,能够满足不同阶段学员的需求。
课程体系的设计需要遵循学习曲线规律。入门课侧重基础操作,进阶课侧重专项技能,高级课侧重设计创作,亲子课侧重体验互动。这种阶梯式的课程设计能够引导用户持续学习,提升平台的长期价值。
光效动画与颜色映射系统

光效动画函数
function vgRay(w: number, i: number): number {
return (w + i * 47) % 100
}
function vgShine(w: number): number {
return w % 2
}
function vgFloatY(w: number, i: number): number {
return Math.sin((w + i * 30) * 0.08) * 4
}
function vgBlink(w: number): number {
return w % 2
}
虽然光效动画函数数量不多(4个),但每一个都经过精心设计,服务于彩绘玻璃的视觉主题。vgRay函数模拟光线穿过玻璃的折射效果,返回0到100的相位值;vgShine函数产生闪烁效果,模拟玻璃在光线下的反光;vgFloatY函数实现轻盈的浮动效果,如同玻璃在微风中轻轻摇曳;vgBlink函数则产生眨眼般的开关效果。
彩绘玻璃的核心美感在于光与色彩的互动。当光线穿过彩色玻璃时,会产生折射、反射、色散等光学现象。虽然移动屏幕无法真正模拟这些物理效果,但通过精心设计的动画函数,可以在视觉上营造出类似的光感和动感。
多色映射系统
function vgGlassColor(c: string): string {
if (c === '钴蓝') {
return '#1D4ED8'
}
if (c === '宝石红') {
return '#BE123C'
}
if (c === '琥珀金' || c === '琥珀') {
return '#D97706'
}
if (c === '翡翠绿') {
return '#047857'
}
if (c === '紫水晶') {
return '#7C3AED'
}
if (c === '海蓝') {
return '#0891B2'
}
if (c === '墨黑') {
return '#312E3F'
}
if (c === '虹彩') {
return '#DB2777'
}
return '#D8D3E8'
}
function vgStyleColor(s: string): string {
if (s === '哥特') {
return '#BE123C'
}
if (s === '蒂芙尼') {
return '#D97706'
}
if (s === '现代') {
return '#1D4ED8'
}
if (s === '拜占庭') {
return '#7C3AED'
}
return '#047857'
}
多色映射系统是这款彩绘玻璃应用的核心技术特色之一。vgGlassColor函数根据玻璃颜色名称返回对应的十六进制颜色值,支持钴蓝、宝石红、琥珀金、翡翠绿、紫水晶、海蓝、墨黑、虹彩等8种颜色,几乎涵盖了所有经典的彩绘玻璃色彩。
vgStyleColor函数则根据作品风格返回对应的主题色:哥特风格用宝石红,蒂芙尼风格用琥珀金,现代风格用钴蓝,拜占庭风格用紫水晶,其他风格用翡翠绿。这种风格-颜色映射系统使得每种艺术风格都有其标志性的视觉色彩,增强了品牌识别度。
颜色映射函数是一种优雅的设计模式。它将颜色名称(业务语义)与颜色值(视觉表现)解耦,使得颜色修改和扩展变得简单。当需要新增一种玻璃颜色时,只需在映射函数中增加一个分支即可,无需改动所有使用颜色的UI代码。
统计计算函数
function vgStyleCount(s: string): number {
let r: number = 0
for (let i = 0; i < VG_WORKS.length; i++) {
if (VG_WORKS[i].style === s) {
r = r + 1
}
}
return r
}
function vgTypeCount(ty: string): number {
let r: number = 0
for (let i = 0; i < VG_TOOLS.length; i++) {
if (VG_TOOLS[i].type === ty) {
r = r + 1
}
}
return r
}
vgStyleCount和vgTypeCount是两个统计类函数,分别用于计算某种风格的作品数量和某种类型的工具数量。这些统计函数为数据可视化提供了基础数据支持,使得应用可以展示风格分布图、工具类型占比等统计图表。
主组件与首页设计
品牌头部与搜索
// 头部品牌栏
Row() {
Column() {
Text('Vitrail').fontSize(22).fontWeight(900).fontColor(VG.primary)
Text('GLASS STUDIO · 彩绘玻璃').fontSize(10).fontColor(VG.sub).letterSpacing(2)
}
.alignItems(HorizontalAlign.Start)
Column().layoutWeight(1)
Row() {
Text('光影艺术').fontSize(12).fontColor(VG.sub)
Column().width(2).height(12).backgroundColor(VG.line).margin({ left: 8, right: 8 })
Text('手工创作').fontSize(10).fontColor(VG.accent).fontWeight(700)
}
}
.width('100%').height(56).padding({ left: 16, right: 16 }).backgroundColor(VG.card)
品牌头部设计延续了艺术风格,“Vitrail”(法语中"彩色玻璃"之意)使用靛蓝色主色,下方是"GLASS STUDIO · 彩绘玻璃"的英中文组合,传递出专业的艺术工作室气质。右侧Slogan"光影艺术 · 手工创作"中,"手工创作"使用琥珀金强调,点明了手工艺术的核心价值。
首页功能架构
首页是应用内容最丰富的页面,包含Banner轮播、品类入口、热门作品、玻璃材料、工具推荐、课程推荐等多个模块。每个模块都采用卡片式设计,在淡紫色背景上错落有致地排列,形成富有节奏感的页面布局。
玻璃光效Banner是首页的视觉焦点,使用渐变色模拟光线穿过彩色玻璃的效果,配合动态光效动画,营造出梦幻般的视觉体验。品类入口则以彩色方块图标的形式展示玻璃、作品、工具、课程四大品类,引导用户快速进入相应板块。
首页设计的核心目标是"发现"——让用户在浏览过程中发现感兴趣的内容,从而激发进一步探索的欲望。通过多样化的内容模块和精心设计的视觉层次,可以有效提升用户的浏览深度和转化率。
应用架构流程图
玻璃Tab深度解析
颜色筛选与材料展示
// 颜色筛选 chips
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '热销', '进口', '经典', '新品', '手工'], (t: string) => {
Text(t).fontSize(12).fontColor(this.filterTag === t ? VG.card : VG.text)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.filterTag === t ? VG.primary : VG.bg)
.onClick(() => {
this.filterTag = t
this.list = vgFilterGlass(t)
})
}, (t: string) => t)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
.width('100%')
.height(36)
玻璃Tab的筛选Chips提供了"全部、热销、进口、经典、新品、手工"六个标签选项,从不同维度对玻璃材料进行分类。用户可以根据自己的需求快速筛选出感兴趣的玻璃类型。
玻璃材料卡片采用双列网格布局,每张卡片顶部是对应颜色的玻璃预览色块,使用vgGlassColor函数根据玻璃颜色名称动态获取颜色值。这种数据驱动的颜色渲染方式使得新增玻璃颜色时无需修改UI代码,只需在映射函数中增加对应颜色即可。
玻璃材料的展示方式对用户体验影响很大。将玻璃的真实颜色作为卡片主视觉,能够让用户直观地感受玻璃的色彩效果,大幅降低选购决策成本。颜色映射函数在其中扮演了关键角色。
透光率与厚度可视化
// 透光率对比
Column() {
Text('透光率对比 (%)').fontSize(13).fontWeight(700).fontColor(VG.dark).margin({ bottom: 12 })
ForEach(this.list.slice(0, 5), (g: VgGlass, i: number) => {
Row() {
Text(g.name.substring(0, 6)).fontSize(10).fontColor(VG.sub).width(60)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(10).borderRadius(5).backgroundColor(VG.bg)
Column().width(vgTransW(g.trans).toString() + '%').height(10).borderRadius(5).backgroundColor(vgGlassColor(g.colorName))
}
.layoutWeight(1)
Text(g.trans.toString()).fontSize(10).fontColor(VG.text).fontWeight(600).width(28)
}
.width('100%').margin({ bottom: 6 })
}, (g: VgGlass) => g.name)
}
.width('100%').padding(16).backgroundColor(VG.card).margin({ top: 8 })
透光率对比图是玻璃Tab的特色数据可视化。每个玻璃的透光率以横向进度条的形式展示,进度条的颜色使用玻璃本身的颜色,通过vgGlassColor(g.colorName)动态获取。这种将产品本身颜色融入数据可视化的设计既美观又直观,用户一眼就能将颜色与透光率对应起来。
厚度分布柱状图则以竖向柱形展示各款玻璃的厚度,柱形高度由vgThickH函数计算得出。厚度是玻璃材料的重要参数,直接影响玻璃的质感和用途,将其可视化有助于用户快速比较和选择。
数据可视化的最高境界是"所见即所得"——图表本身就能传递信息,而不需要用户费力解读。将玻璃颜色融入透光率图表,就是一个很好的例子:彩色进度条不仅展示了透光率数值,还同时展示了玻璃本身的颜色,一举两得。
作品Tab与风格体系
作品风格分类
// 风格筛选
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '哥特', '蒂芙尼', '现代', '新艺术', '拜占庭'], (s: string) => {
Text(s).fontSize(12).fontColor(this.filterStyle === s ? '#FFFFFF' : VG.text)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.filterStyle === s ? vgStyleColor(s) : VG.bg)
.onClick(() => {
this.filterStyle = s
})
}, (s: string) => s)
}
.padding({ left: 12, right: 12 })
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)
作品Tab的风格筛选是vgStyleColor函数的典型应用场景。每个风格标签的选中状态背景色就是该风格对应的主题色:哥特是宝石红、蒂芙尼是琥珀金、现代是钴蓝、拜占庭是紫水晶。这种设计让每个风格都有了自己的视觉标识,用户可以快速建立风格与颜色的对应关系。
作品列表采用卡片式布局,每张作品卡片包含作品名称、风格标签、尺寸、玻璃片数、颜色数、价格等信息。玻璃片数和颜色数以数据条的形式可视化展示,让用户可以直观感受作品的复杂度。
风格是艺术品类应用的核心分类维度。为每种风格赋予独特的品牌色,不仅能提升视觉美感,更能在用户心中建立深刻的品牌记忆。这就像提到印象派就想到莫奈的光影、提到哥特就想到神秘的教堂一样。
风格分布统计
// 风格分布
Column() {
Text('风格分布').fontSize(13).fontWeight(700).fontColor(VG.dark).margin({ bottom: 12 })
Column() {
Row() {
Text('哥特').fontSize(11).fontColor(VG.sub).width(56)
Stack({ alignContent: Alignment.Start }) {
Column().width('100%').height(8).borderRadius(4).backgroundColor(VG.bg)
Column().width((vgStyleCount('哥特') / VG_WORKS.length * 100).toString() + '%').height(8).borderRadius(4).backgroundColor(vgStyleColor('哥特'))
}
.layoutWeight(1)
Text(vgStyleCount('哥特').toString()).fontSize(11).fontColor(VG.text).fontWeight(600).width(28)
}
.margin({ bottom: 8 })
// ... 更多风格
}
.width('100%')
}
.width('100%').padding(16).backgroundColor(VG.card).margin({ top: 8 })
风格分布统计是作品Tab的数据可视化模块,使用横向进度条展示各种风格作品的数量分布。每个进度条的颜色使用对应风格的主题色,与上方的筛选标签形成呼应。vgStyleCount函数负责统计每种风格的作品数量,为图表提供数据支持。
这种统计图表不仅具有装饰性,更具有实际的信息价值——用户可以通过它快速了解平台上各种风格的作品数量分布,从而判断平台的风格偏向和自己的喜好匹配度。
课程Tab与培训体系
课程级别与卡片设计
// 级别筛选
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '入门', '进阶', '高级', '亲子'], (l: string) => {
Text(l).fontSize(12).fontColor(this.filterLevel === l ? VG.card : VG.text)
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor(this.filterLevel === l ? VG.primary : VG.bg)
.onClick(() => {
this.filterLevel = l
})
}, (l: string) => l)
}
.padding({ left: 12, right: 12 })
}
课程Tab的筛选提供了"全部、入门、进阶、高级、亲子"五个级别选项,对应完整的学习路径体系。用户可以根据自己的水平选择合适级别的课程。
课程卡片是课程Tab的核心UI组件。每张卡片包含课程名称、级别标签、课时数、时长、学员人数、价格等信息。课时数和时长以图标+数字的形式展示,学员人数则反映了课程的受欢迎程度。卡片底部的价格使用琥珀金强调色突出显示,引导用户购买。
课程产品的信息展示需要平衡专业性和易懂性。课时数、时长、学员数这些数据能够帮助用户判断课程的含金量和受欢迎程度,但过多的数据也可能造成信息过载。合理的信息层级设计至关重要。
课程数据可视化
// 课时对比
Column() {
Text('课时对比').fontSize(13).fontWeight(700).fontColor(VG.dark).margin({ bottom: 12 })
Row({ space: 6 }) {
ForEach(this.courseList.slice(0, 6), (c: VgCourse, i: number) => {
Column() {
Column().width(20).height(vgLessonH(c.lessons)).borderRadius(4).backgroundColor(VG.primary)
Text(c.lessons.toString() + '节').fontSize(8).fontColor(VG.sub).margin({ top: 4 })
}
.alignItems(HorizontalAlign.Center)
}, (c: VgCourse) => c.name)
}
.width('100%').justifyContent(FlexAlign.SpaceAround)
}
.width('100%').padding(16).backgroundColor(VG.card).margin({ top: 8, bottom: 8 })
课时对比柱状图直观展示了各门课程的课时数量差异,柱形高度由vgLessonH函数计算得出。学员人数分布图则展示了各门课程的受欢迎程度,使用横向进度条的形式呈现。
这些数据可视化图表不仅美化了页面,更重要的是帮助用户快速比较各门课程的规模和受欢迎程度,从而做出更明智的选择。对于教育类产品来说,这种数据透明化的做法能够有效提升用户的信任感。
"我的"Tab与会员体系
订单与收藏
// 订单列表
Column() {
Text('我的订单').fontSize(13).fontWeight(700).fontColor(VG.dark).margin({ bottom: 8 })
ForEach(this.orders, (o: VgOrder, i: number) => {
Row() {
Column() {
Text(o.name).fontSize(13).fontWeight(600).fontColor(VG.text)
Text(o.id + ' · ' + o.date).fontSize(10).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column() {
Text('¥' + o.amount.toString()).fontSize(13).fontWeight(700).fontColor(VG.text)
Text(o.status).fontSize(10).fontColor(vgStatusColor(o.status)).margin({ top: 2 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%').padding({ top: 8, bottom: 8 })
.border({ width: { bottom: 1 }, color: VG.line })
}, (o: VgOrder) => o.id)
}
订单列表展示了用户的购买记录,状态颜色通过vgStatusColor函数动态计算。这款应用的订单状态有三种:"已完成"使用翡翠绿色,"制作中"使用琥珀金色,"待开课"使用靛蓝色。三种状态分别对应绿色(完成)、橙色(进行中)、蓝色(等待中)的语义,符合用户的直觉认知。
订单状态的颜色设计应该遵循通用的语义惯例:绿色表示完成/成功,橙色/黄色表示进行中/警告,蓝色/灰色表示等待/未开始。这种符合用户直觉的设计能够降低认知成本,提升使用效率。
技术点对比表格
| 技术维度 | 玻璃艺术 (VG) | 盆景艺术 (BN) | 皮具工坊 (LT) | 纹身潮流 (IW) |
|---|---|---|---|---|
| 代码规模 | 最大(4458行) | 2274行 | 2251行 | 2124行 |
| 设计风格 | 彩绘玻璃艺术风 | 新中式禅意 | 复古手作风 | 潮流赛博风 |
| 主题模式 | 浅色多彩 | 浅色单彩 | 浅色单彩 | 深色霓虹 |
| 主色调 | 靛蓝 #4338CA | 苔藓绿 #5B7C5A | 马鞍棕 #8B4513 | 荧光青 #00FFFF |
| 强调色 | 琥珀金 #D97706 | 陶土棕 #B5651D | 焦糖橙 #D2691E | 电粉 #FF1493 |
| 颜色系统 | 8色玻璃映射+风格映射 | 9色主题令牌 | 9色主题令牌 | 9色主题令牌 |
| 特色功能 | 课程培训体系 | 展览拍卖 | 匠人定制 | 图案设计社区 |
| 动画数量 | 4个 | 4个 | 6个 | 6个 |
| 数据接口数 | 7个 | 8个 | 8个 | 8个 |
| Tab数量 | 6个 | 6个 | 6个 | 6个 |
| 图标方案 | 特殊字符符号 | 资源图片 | 资源图片 | 资源图片 |
| 筛选维度 | 风格/级别/标签 | 标签分类 | 标签分类 | 风格标签 |
安装DevEco Studio程序

选择目标安装目录:

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

新建一个空白模板:

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

完整代码:
// 场景:彩绘玻璃 / 花窗 / 蒂芙尼灯罩 工坊商城(得物风格)
// 主题:靛蓝 × 琥珀金 × 宝石红(浅色系,彩色玻璃点缀)
// 6 tab:首页 / 玻璃 / 作品 / 工具 / 课程 / 我的
interface VgTheme {
bg: string
primary: string
accent: string
card: string
text: string
sub: string
line: string
glow: string
dark: string
}
interface VgTab {
name: string
icon: string
}
interface VgGlass {
name: string
colorName: string
texture: string
thickness: number
trans: number
price: number
stock: number
sold: number
tag: string
}
interface VgWork {
name: string
style: string
size: string
panels: number
colorCount: number
price: number
sold: number
tag: string
}
interface VgTool {
name: string
type: string
brand: string
material: string
use: string
price: number
sold: number
}
interface VgCourse {
name: string
level: string
lessons: number
hours: number
students: number
price: number
}
interface VgOrder {
id: string
name: string
date: string
amount: number
status: string
}
interface VgFav {
name: string
kind: string
price: number
note: string
}
interface VgMonth {
month: string
amount: number
}
const VG: VgTheme = {
bg: '#F1EFF7',
primary: '#4338CA',
accent: '#D97706',
card: '#FFFFFF',
text: '#241E3E',
sub: '#837CA8',
line: '#E2DDF0',
glow: '#7C6FF0',
dark: '#241E3E'
}
const VG_RUBY: string = '#BE123C'
const VG_EMERALD: string = '#047857'
const VG_COBALT: string = '#1D4ED8'
const VG_AMBER: string = '#D97706'
const VG_TABS: VgTab[] = [
{ name: '首页', icon: '◈' },
{ name: '玻璃', icon: '▩' },
{ name: '作品', icon: '✦' },
{ name: '工具', icon: '⚒' },
{ name: '课程', icon: '✧' },
{ name: '我的', icon: '◉' }
]
const VG_GLASS: VgGlass[] = [
{ name: '钴蓝教堂玻璃', colorName: '钴蓝', texture: '水纹', thickness: 3, trans: 78, price: 42, stock: 120, sold: 356, tag: '热销' },
{ name: '宝石红玻璃', colorName: '宝石红', texture: '锤纹', thickness: 3, trans: 72, price: 45, stock: 86, sold: 312, tag: '热销' },
{ name: '琥珀金玻璃', colorName: '琥珀金', texture: '猫眼', thickness: 4, trans: 66, price: 52, stock: 64, sold: 288, tag: '进口' },
{ name: '翡翠绿玻璃', colorName: '翡翠绿', texture: '麻纹', thickness: 3, trans: 75, price: 38, stock: 150, sold: 274, tag: '经典' },
{ name: '虹彩玻璃', colorName: '虹彩', texture: '镀膜', thickness: 4, trans: 58, price: 68, stock: 40, sold: 245, tag: '进口' },
{ name: '乳白雪花玻璃', colorName: '乳白', texture: '雪花', thickness: 3, trans: 52, price: 35, stock: 98, sold: 226, tag: '经典' },
{ name: '紫水晶玻璃', colorName: '紫水晶', texture: '水滴', thickness: 5, trans: 61, price: 58, stock: 55, sold: 208, tag: '新品' },
{ name: '海蓝起皱玻璃', colorName: '海蓝', texture: '起皱', thickness: 3, trans: 70, price: 40, stock: 110, sold: 196, tag: '新品' },
{ name: '琥珀手吹玻璃', colorName: '琥珀', texture: '手吹', thickness: 4, trans: 64, price: 76, stock: 32, sold: 178, tag: '手工' },
{ name: '墨黑镜面玻璃', colorName: '墨黑', texture: '镜面', thickness: 5, trans: 45, price: 62, stock: 48, sold: 154, tag: '手工' }
]
const VG_WORKS: VgWork[] = [
{ name: '哥特玫瑰窗', style: '哥特', size: '60×60cm', panels: 12, colorCount: 5, price: 1280, sold: 46, tag: '镇店' },
{ name: '蒂芙尼蜻蜓灯罩', style: '蒂芙尼', size: '40×25cm', panels: 9, colorCount: 4, price: 980, sold: 68, tag: '热销' },
{ name: '现代向日葵挂屏', style: '现代', size: '50×35cm', panels: 8, colorCount: 3, price: 720, sold: 92, tag: '热销' },
{ name: '新艺术鸢尾竖窗', style: '新艺术', size: '35×80cm', panels: 10, colorCount: 4, price: 1180, sold: 55, tag: '精品' },
{ name: '拜占庭穹顶灯', style: '拜占庭', size: 'Ø45cm', panels: 14, colorCount: 6, price: 1580, sold: 33, tag: '精品' },
{ name: '草原风格台灯', style: '新艺术', size: '30×20cm', panels: 7, colorCount: 3, price: 660, sold: 110, tag: '人气' },
{ name: '几何蝴蝶挂饰', style: '现代', size: '25×25cm', panels: 6, colorCount: 3, price: 380, sold: 186, tag: '人气' },
{ name: '海浪珠帘屏风', style: '现代', size: '80×40cm', panels: 11, colorCount: 5, price: 1080, sold: 62, tag: '新品' }
]
const VG_TOOLS: VgTool[] = [
{ name: '专业玻璃割刀', type: '切割', brand: 'Toyo', material: '碳化钨轮', use: '裁切玻璃', price: 168, sold: 428 },
{ name: '恒温焊锡台', type: '焊接', brand: 'Weller', material: '紫铜烙铁头', use: '锡线焊接', price: 298, sold: 356 },
{ name: '金刚石磨边机', type: '打磨', brand: 'Glastar', material: '金刚砂磨盘', use: '边缘打磨', price: 1180, sold: 128 },
{ name: '铜箔胶带', type: '包边', brand: 'Edco', material: '电解铜箔', use: '玻璃包边', price: 26, sold: 962 },
{ name: '平口玻璃钳', type: '夹持', brand: '世纪', material: '高碳钢', use: '掰断玻璃', price: 58, sold: 734 },
{ name: '助焊膏', type: '焊接', brand: 'Gedas', material: '松香脂', use: '助焊防氧化', price: 22, sold: 688 },
{ name: '油壶割刀笔', type: '切割', brand: '红鹰', material: '合金刀轮', use: '曲线裁切', price: 96, sold: 512 },
{ name: '花样剪刀', type: '切割', brand: '世纪', material: '弹簧钢', use: '纸样剪裁', price: 45, sold: 465 }
]
const VG_COURSES: VgCourse[] = [
{ name: '彩绘玻璃入门八讲', level: '入门', lessons: 8, hours: 16, students: 326, price: 398 },
{ name: '蒂芙尼灯罩进阶班', level: '进阶', lessons: 12, hours: 30, students: 158, price: 1280 },
{ name: '割刀手法专训课', level: '入门', lessons: 4, hours: 8, students: 288, price: 268 },
{ name: '哥特花窗设计课', level: '高级', lessons: 10, hours: 26, students: 96, price: 1680 },
{ name: '玻璃彩绘着色课', level: '进阶', lessons: 6, hours: 12, students: 204, price: 588 },
{ name: '亲子玻璃体验营', level: '亲子', lessons: 3, hours: 5, students: 412, price: 198 }
]
const VG_ORDERS: VgOrder[] = [
{ id: 'VG20260801', name: '蒂芙尼蜻蜓灯罩', date: '08-01', amount: 980, status: '已完成' },
{ id: 'VG20260805', name: '钴蓝教堂玻璃×5', date: '08-05', amount: 210, status: '制作中' },
{ id: 'VG20260809', name: '专业玻璃割刀', date: '08-09', amount: 168, status: '已完成' },
{ id: 'VG20260812', name: '亲子玻璃体验营', date: '08-12', amount: 198, status: '待开课' },
{ id: 'VG20260816', name: '几何蝴蝶挂饰', date: '08-16', amount: 380, status: '制作中' },
{ id: 'VG20260820', name: '铜箔胶带×6', date: '08-20', amount: 156, status: '已完成' }
]
const VG_FAVS: VgFav[] = [
{ name: '哥特玫瑰窗', kind: '作品', price: 1280, note: '镇店之宝' },
{ name: '虹彩玻璃', kind: '玻璃', price: 68, note: '镀膜超美' },
{ name: '恒温焊锡台', kind: '工具', price: 298, note: '新手必备' },
{ name: '海浪珠帘屏风', kind: '作品', price: 1080, note: '客厅绝配' },
{ name: '琥珀手吹玻璃', kind: '玻璃', price: 76, note: '手吹纹理' },
{ name: '哥特花窗设计课', kind: '课程', price: 1680, note: '想学设计' }
]
const VG_MONTHS: VgMonth[] = [
{ month: '3月', amount: 560 },
{ month: '4月', amount: 890 },
{ month: '5月', amount: 420 },
{ month: '6月', amount: 1260 },
{ month: '7月', amount: 780 },
{ month: '8月', amount: 1080 }
]
function vgRay(w: number, i: number): number {
return (w + i * 47) % 100
}
function vgShine(w: number): number {
return w % 2
}
function vgFloatY(w: number, i: number): number {
return Math.sin((w + i * 30) * 0.08) * 4
}
function vgBlink(w: number): number {
return w % 2
}
function vgFilterGlass(k: string): VgGlass[] {
let r: VgGlass[] = []
for (let i = 0; i < VG_GLASS.length; i++) {
if (k === '全部' || VG_GLASS[i].tag === k) {
r.push(VG_GLASS[i])
}
}
return r
}
function vgSoldRank(): VgGlass[] {
let r: VgGlass[] = VG_GLASS.slice()
r.sort((a: VgGlass, b: VgGlass) => b.sold - a.sold)
return r
}
function vgWorkRank(): VgWork[] {
let r: VgWork[] = VG_WORKS.slice()
r.sort((a: VgWork, b: VgWork) => b.sold - a.sold)
return r
}
function vgToolRank(): VgTool[] {
let r: VgTool[] = VG_TOOLS.slice()
r.sort((a: VgTool, b: VgTool) => b.sold - a.sold)
return r
}
function vgTransW(t: number): number {
return t
}
function vgThickH(t: number): number {
return t * 20
}
function vgPanelW(p: number): number {
return p * 8
}
function vgLessonH(l: number): number {
return l * 9
}
function vgStudentW(s: number): number {
return Math.min(100, s / 5)
}
function vgMonthH(m: number): number {
return Math.min(120, m / 12)
}
function vgStatusColor(s: string): string {
if (s === '已完成') {
return '#047857'
}
if (s === '制作中') {
return '#D97706'
}
return '#4338CA'
}
function vgStyleColor(s: string): string {
if (s === '哥特') {
return '#BE123C'
}
if (s === '蒂芙尼') {
return '#D97706'
}
if (s === '现代') {
return '#1D4ED8'
}
if (s === '拜占庭') {
return '#7C3AED'
}
return '#047857'
}
function vgGlassColor(c: string): string {
if (c === '钴蓝') {
return '#1D4ED8'
}
if (c === '宝石红') {
return '#BE123C'
}
if (c === '琥珀金' || c === '琥珀') {
return '#D97706'
}
if (c === '翡翠绿') {
return '#047857'
}
if (c === '紫水晶') {
return '#7C3AED'
}
if (c === '海蓝') {
return '#0891B2'
}
if (c === '墨黑') {
return '#312E3F'
}
if (c === '虹彩') {
return '#DB2777'
}
return '#D8D3E8'
}
function vgStyleCount(s: string): number {
let r: number = 0
for (let i = 0; i < VG_WORKS.length; i++) {
if (VG_WORKS[i].style === s) {
r = r + 1
}
}
return r
}
function vgTypeCount(ty: string): number {
let r: number = 0
for (let i = 0; i < VG_TOOLS.length; i++) {
if (VG_TOOLS[i].type === ty) {
r = r + 1
}
}
return r
}
function vgGlassByName(n: string): VgGlass {
let r: VgGlass = VG_GLASS[0]
for (let i = 0; i < VG_GLASS.length; i++) {
if (VG_GLASS[i].name === n) {
r = VG_GLASS[i]
}
}
return r
}
function vgWorkByName(n: string): VgWork {
let r: VgWork = VG_WORKS[0]
for (let i = 0; i < VG_WORKS.length; i++) {
if (VG_WORKS[i].name === n) {
r = VG_WORKS[i]
}
}
return r
}
function vgToolByName(n: string): VgTool {
let r: VgTool = VG_TOOLS[0]
for (let i = 0; i < VG_TOOLS.length; i++) {
if (VG_TOOLS[i].name === n) {
r = VG_TOOLS[i]
}
}
return r
}
function vgCourseByName(n: string): VgCourse {
let r: VgCourse = VG_COURSES[0]
for (let i = 0; i < VG_COURSES.length; i++) {
if (VG_COURSES[i].name === n) {
r = VG_COURSES[i]
}
}
return r
}
@Entry
@Component
struct VgApp {
@State currentTab: number = 0
@State showCustom: boolean = false
@State showExpo: boolean = false
@State showClass: boolean = false
@State showComm: boolean = false
@Builder
modalOverlay() {
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(36,30,62,0.62)')
.position({ x: 0, y: 0 })
.zIndex(999)
.onClick(() => {
this.showCustom = false
this.showExpo = false
this.showClass = false
this.showComm = false
})
}
build() {
Stack() {
Column() {
Column() {
Row() {
Row({ space: 8 }) {
Row({ space: 3 }) {
Column().width(10).height(10).backgroundColor(VG_AMBER).borderRadius(2)
Column().width(10).height(10).backgroundColor(VG_RUBY).borderRadius(2)
Column().width(10).height(10).backgroundColor(VG_COBALT).borderRadius(2)
}
Column() {
Text('VITRAIL')
.fontSize(22)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
.letterSpacing(2)
Text('彩绘玻璃艺术工坊')
.fontSize(11)
.fontColor('rgba(255,255,255,0.72)')
.margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
}
.alignItems(VerticalAlign.Center)
Row({ space: 10 }) {
Column() {
Text('♢').fontSize(18).fontColor('#FFFFFF')
Text('鉴赏').fontSize(9).fontColor('rgba(255,255,255,0.72)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.showExpo = true
})
Column() {
Text('☰').fontSize(18).fontColor('#FFFFFF')
Text('更多').fontSize(9).fontColor('rgba(255,255,255,0.72)').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.showComm = true
})
}
.alignItems(VerticalAlign.Center)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
Row() {
Text('⌕').fontSize(16).fontColor('rgba(255,255,255,0.8)')
Text('搜索玻璃 / 作品 / 课程')
.fontSize(13)
.fontColor('rgba(255,255,255,0.6)')
.margin({ left: 8 })
}
.width('100%')
.height(38)
.borderRadius(19)
.backgroundColor('rgba(255,255,255,0.16)')
.margin({ top: 12 })
.alignItems(VerticalAlign.Center)
.padding({ left: 14, right: 14 })
}
.width('100%')
.padding({ left: 16, right: 16, top: 14, bottom: 12 })
.backgroundColor(VG.primary)
Column() {
if (this.currentTab === 0) {
VgHomeTab({
onCustom: () => {
this.showCustom = true
},
onExpo: () => {
this.showExpo = true
},
onClass: () => {
this.showClass = true
},
onComm: () => {
this.showComm = true
}
})
} else if (this.currentTab === 1) {
VgGlassTab()
} else if (this.currentTab === 2) {
VgWorkTab()
} else if (this.currentTab === 3) {
VgToolTab()
} else if (this.currentTab === 4) {
VgCourseTab()
} else {
VgMineTab()
}
}
.layoutWeight(1)
.width('100%')
Row() {
ForEach(VG_TABS, (t: VgTab, i: number) => {
Column({ space: 3 }) {
Text(t.icon)
.fontSize(20)
.fontColor(this.currentTab === i ? VG.primary : VG.sub)
Text(t.name)
.fontSize(10)
.fontColor(this.currentTab === i ? VG.primary : VG.sub)
.fontWeight(this.currentTab === i ? FontWeight.Bold : FontWeight.Normal)
}
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.currentTab = i
})
}, (t: VgTab) => t.name)
}
.width('100%')
.padding({ top: 8, bottom: 10 })
.backgroundColor(VG.card)
.justifyContent(FlexAlign.SpaceAround)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor(VG.bg)
if (this.showCustom) {
Stack() {
this.modalOverlay()
Column() {
Text('定制花窗')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('告诉我们你想要的彩窗图案')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 10 }) {
Column() {
Text('作品风格').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Row({ space: 8 }) {
Text('哥特').fontSize(12).fontColor(VG.card).backgroundColor(VG_RUBY).padding({ left: 12, right: 12, top: 6, bottom: 6 }).borderRadius(12)
Text('蒂芙尼').fontSize(12).fontColor(VG.card).backgroundColor(VG_AMBER).padding({ left: 12, right: 12, top: 6, bottom: 6 }).borderRadius(12)
Text('现代').fontSize(12).fontColor(VG.card).backgroundColor(VG_COBALT).padding({ left: 12, right: 12, top: 6, bottom: 6 }).borderRadius(12)
Text('新艺术').fontSize(12).fontColor(VG.sub).backgroundColor(VG.bg).padding({ left: 12, right: 12, top: 6, bottom: 6 }).borderRadius(12)
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('尺寸(cm)').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Row({ space: 10 }) {
Column() {
TextInput({ placeholder: '宽' })
.fontSize(13)
.height(36)
.backgroundColor(VG.bg)
.padding({ left: 0 })
}
.width(72)
.borderRadius(10)
.backgroundColor(VG.bg)
.padding({ left: 10 })
Text('×').fontSize(14).fontColor(VG.sub)
Column() {
TextInput({ placeholder: '高' })
.fontSize(13)
.height(36)
.backgroundColor(VG.bg)
.padding({ left: 0 })
}
.width(72)
.borderRadius(10)
.backgroundColor(VG.bg)
.padding({ left: 10 })
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('玻璃色系').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Row({ space: 8 }) {
Column().width(24).height(24).borderRadius(12).backgroundColor(VG_COBALT).border({ width: 2, color: VG.line })
Column().width(24).height(24).borderRadius(12).backgroundColor(VG_RUBY).border({ width: 2, color: VG.line })
Column().width(24).height(24).borderRadius(12).backgroundColor(VG_AMBER).border({ width: 2, color: VG.line })
Column().width(24).height(24).borderRadius(12).backgroundColor(VG_EMERALD).border({ width: 2, color: VG.line })
Column().width(24).height(24).borderRadius(12).backgroundColor('#7C3AED').border({ width: 2, color: VG.line })
Text('可多选').fontSize(10).fontColor(VG.sub).margin({ left: 4 })
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('参考图片').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Row({ space: 8 }) {
Column() {
Text('+').fontSize(20).fontColor(VG.sub)
}
.width(56)
.height(56)
.borderRadius(10)
.backgroundColor(VG.bg)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('上传手稿或参考图,匠人 24h 内评估报价')
.fontSize(10)
.fontColor(VG.sub)
.margin({ left: 4 })
.layoutWeight(1)
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Row({ space: 10 }) {
Text('取消')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showCustom = false
})
Text('提交定制')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showCustom = false
})
}
.width('100%')
.margin({ top: 18 })
}
.width('100%')
.margin({ top: 16 })
}
.width('86%')
.constraintSize({ maxHeight: '80%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showExpo) {
Stack() {
this.modalOverlay()
Column() {
Text('光之巡展')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('2026 彩绘玻璃艺术展 · 上海站')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 10 }) {
Row() {
Text('展期').fontSize(12).fontColor(VG.sub)
Text('09.12 - 10.20').fontSize(12).fontColor(VG.text).fontWeight(FontWeight.Medium)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.justifyContent(FlexAlign.SpaceBetween)
Row() {
Text('场馆').fontSize(12).fontColor(VG.sub)
Text('西岸穹顶艺术中心').fontSize(12).fontColor(VG.text).fontWeight(FontWeight.Medium)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.justifyContent(FlexAlign.SpaceBetween)
Row() {
Text('展品').fontSize(12).fontColor(VG.sub)
Text('128 件 · 含 6 件百年古董花窗').fontSize(12).fontColor(VG.text).fontWeight(FontWeight.Medium)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.justifyContent(FlexAlign.SpaceBetween)
Row() {
Text('早鸟票').fontSize(12).fontColor(VG.sub)
Text('¥68(原价 ¥98)').fontSize(12).fontColor(VG_RUBY).fontWeight(FontWeight.Bold)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.margin({ top: 14 })
Row({ space: 10 }) {
Text('关闭')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showExpo = false
})
Text('抢早鸟票')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG_RUBY)
.onClick(() => {
this.showExpo = false
})
}
.width('100%')
.margin({ top: 18 })
}
.width('82%')
.constraintSize({ maxHeight: '75%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showClass) {
Stack() {
this.modalOverlay()
Column() {
Text('工坊课程')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('零基础也能做出第一盏 Tiffany 灯')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 12 }) {
Row({ space: 12 }) {
Column() {
Text('Ⅰ').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}
.width(44)
.height(44)
.borderRadius(22)
.backgroundColor(VG_AMBER)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Column() {
Text('体验课 1 节').fontSize(14).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('割刀 + 焊锡全流程感受').fontSize(11).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥198').fontSize(13).fontColor(VG_AMBER).fontWeight(FontWeight.Bold)
}
.width('100%')
Row({ space: 12 }) {
Column() {
Text('Ⅱ').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}
.width(44)
.height(44)
.borderRadius(22)
.backgroundColor(VG_COBALT)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Column() {
Text('系统课 8 节').fontSize(14).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('独立完成 35cm 挂屏作品').fontSize(11).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥398').fontSize(13).fontColor(VG_COBALT).fontWeight(FontWeight.Bold)
}
.width('100%')
Row({ space: 12 }) {
Column() {
Text('Ⅲ').fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
}
.width(44)
.height(44)
.borderRadius(22)
.backgroundColor(VG_RUBY)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Column() {
Text('匠人班 12 节').fontSize(14).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('蒂芙尼灯罩从图纸到点灯').fontSize(11).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥1280').fontSize(13).fontColor(VG_RUBY).fontWeight(FontWeight.Bold)
}
.width('100%')
}
.width('100%')
.margin({ top: 14 })
Text('报名后 30 天内可约,材料全包')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 12 })
Row({ space: 10 }) {
Text('再想想')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showClass = false
})
Text('去报名')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showClass = false
})
}
.width('100%')
.margin({ top: 16 })
}
.width('82%')
.constraintSize({ maxHeight: '70%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showComm) {
Stack() {
this.modalOverlay()
Column() {
Text('玻璃社区')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('2.4 万名玻璃爱好者在这里交流')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 10 }) {
Row({ space: 10 }) {
Text('📷').fontSize(18)
Column() {
Text('作品晒图').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('今日 +86 张').fontSize(10).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('›').fontSize(16).fontColor(VG.sub)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.alignItems(VerticalAlign.Center)
Row({ space: 10 }) {
Text('💡').fontSize(18)
Column() {
Text('技巧问答').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('焊锡发亮的秘密').fontSize(10).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('›').fontSize(16).fontColor(VG.sub)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.alignItems(VerticalAlign.Center)
Row({ space: 10 }) {
Text('🏷').fontSize(18)
Column() {
Text('玻璃转卖').fontSize(13).fontColor(VG.text).fontWeight(FontWeight.Medium)
Text('闲置玻璃板半价出').fontSize(10).fontColor(VG.sub).margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('›').fontSize(16).fontColor(VG.sub)
}
.width('100%')
.padding(12)
.borderRadius(10)
.backgroundColor(VG.bg)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.margin({ top: 14 })
Text('知道了')
.fontSize(14)
.fontColor('#FFFFFF')
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.margin({ top: 16 })
.onClick(() => {
this.showComm = false
})
}
.width('78%')
.constraintSize({ maxHeight: '60%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
}
}
@Component
struct VgHomeTab {
@State wave: number = 0
@State timer: number = -1
@State showDetail: boolean = false
@State selName: string = ''
onCustom: () => void = () => {}
onExpo: () => void = () => {}
onClass: () => void = () => {}
onComm: () => void = () => {}
aboutToAppear() {
this.timer = setInterval(() => {
this.wave = this.wave + 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder
modalOverlay() {
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(36,30,62,0.62)')
.position({ x: 0, y: 0 })
.zIndex(999)
.onClick(() => {
this.showDetail = false
})
}
build() {
Stack() {
Scroll() {
Column() {
Column() {
Row({ space: 6 }) {
Column()
.width(36)
.height(72)
.backgroundColor('rgba(255,255,255,0.22)')
.borderRadius(18)
.translate({ x: vgRay(this.wave, 0) * 2.2 })
Column()
.width(24)
.height(72)
.backgroundColor('rgba(217,119,6,0.35)')
.borderRadius(12)
.translate({ x: vgRay(this.wave, 1) * 1.8 })
Column()
.width(16)
.height(72)
.backgroundColor('rgba(30,78,216,0.4)')
.borderRadius(8)
.translate({ x: vgRay(this.wave, 2) * 1.4 })
Column()
.width(10)
.height(72)
.backgroundColor('rgba(190,18,60,0.45)')
.borderRadius(5)
.translate({ x: vgRay(this.wave, 3) })
}
.width('100%')
.height(72)
.justifyContent(FlexAlign.Center)
Text('让光穿过你家的窗')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.margin({ top: 14 })
Text('每片玻璃都在工坊手裁手焊')
.fontSize(12)
.fontColor('rgba(255,255,255,0.75)')
.margin({ top: 6 })
Row({ space: 8 }) {
Text('128 年工坊传承')
.fontSize(10)
.fontColor('#FFFFFF')
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.borderRadius(10)
.backgroundColor('rgba(217,119,6,0.85)')
Text('含古董玻璃')
.fontSize(10)
.fontColor('#FFFFFF')
.padding({ left: 10, right: 10, top: 5, bottom: 5 })
.borderRadius(10)
.backgroundColor('rgba(190,18,60,0.85)')
}
.margin({ top: 12 })
}
.width('100%')
.padding({ top: 24, bottom: 24, left: 16, right: 16 })
.borderRadius(18)
.backgroundColor(VG.primary)
.margin({ left: 14, right: 14, top: 12 })
Row({ space: 12 }) {
Column() {
Stack() {
Column()
.width(64)
.height(64)
.borderRadius(32)
.backgroundColor(VG.bg)
.border({ width: 4, color: VG_COBALT })
Column()
.width(4)
.height(24)
.backgroundColor(VG.text)
.borderRadius(2)
.rotate({ angle: (this.wave * 3) % 360 })
}
.width(64)
.height(64)
Text('拼块仪表')
.fontSize(11)
.fontColor(VG.sub)
.margin({ top: 6 })
Text('平均 9.6 块/件')
.fontSize(12)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
Column({ space: 8 }) {
Row({ space: 6 }) {
Column().width(12).height(12).borderRadius(3).backgroundColor(VG_RUBY)
Text('宝石红').fontSize(10).fontColor(VG.sub)
}
Row({ space: 6 }) {
Column().width(12).height(12).borderRadius(3).backgroundColor(VG_COBALT)
Text('钴蓝').fontSize(10).fontColor(VG.sub)
}
Row({ space: 6 }) {
Column().width(12).height(12).borderRadius(3).backgroundColor(VG_AMBER)
Text('琥珀金').fontSize(10).fontColor(VG.sub)
}
Row({ space: 6 }) {
Column().width(12).height(12).borderRadius(3).backgroundColor(VG_EMERALD)
Text('翡翠绿').fontSize(10).fontColor(VG.sub)
}
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding({ left: 14, right: 14 })
.margin({ top: 12 })
Row({ space: 10 }) {
Column() {
Text('128')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.primary)
Text('在售玻璃')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(12)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
Column() {
Text('56')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG_RUBY)
Text('精品作品')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(12)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
Column() {
Text('2.4w')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG_AMBER)
Text('社区成员')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(12)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
Column() {
Text('12')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG_EMERALD)
Text('驻坊匠人')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(12)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.padding({ left: 14, right: 14 })
.margin({ top: 12 })
Text('工坊服务')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 16 })
Row({ space: 10 }) {
Column({ space: 6 }) {
Column() {
Text('✦').fontSize(20).fontColor('#FFFFFF')
}
.width(44)
.height(44)
.borderRadius(14)
.backgroundColor(VG.primary)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('定制花窗')
.fontSize(11)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.onCustom()
})
Column({ space: 6 }) {
Column() {
Text('♢').fontSize(20).fontColor('#FFFFFF')
}
.width(44)
.height(44)
.borderRadius(14)
.backgroundColor(VG_RUBY)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('光之巡展')
.fontSize(11)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.onExpo()
})
Column({ space: 6 }) {
Column() {
Text('✧').fontSize(20).fontColor('#FFFFFF')
}
.width(44)
.height(44)
.borderRadius(14)
.backgroundColor(VG_AMBER)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('工坊课程')
.fontSize(11)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.onClass()
})
Column({ space: 6 }) {
Column() {
Text('◉').fontSize(20).fontColor('#FFFFFF')
}
.width(44)
.height(44)
.borderRadius(14)
.backgroundColor(VG_EMERALD)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('玻璃社区')
.fontSize(11)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.card)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.onComm()
})
}
.width('100%')
.padding({ left: 14, right: 14 })
.margin({ top: 10 })
Text('玻璃热销榜')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 18 })
Column({ space: 10 }) {
ForEach(vgSoldRank(), (g: VgGlass, i: number) => {
Row({ space: 12 }) {
Text((i + 1).toString())
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(i < 3 ? '#FFFFFF' : VG.sub)
.width(26)
.height(26)
.textAlign(TextAlign.Center)
.borderRadius(13)
.backgroundColor(i === 0 ? VG_RUBY : (i === 1 ? VG_AMBER : (i === 2 ? VG_COBALT : VG.bg)))
Column()
.width(44)
.height(44)
.borderRadius(10)
.backgroundColor(vgGlassColor(g.colorName))
.opacity(vgBlink(this.wave) === 0 ? 1 : 0.88)
Column() {
Text(g.name)
.fontSize(13)
.fontWeight(FontWeight.Medium)
.fontColor(VG.text)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(g.texture + '纹 · ' + g.thickness + 'mm · 已售 ' + g.sold)
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥' + g.price)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(VG_RUBY)
}
.width('100%')
.padding(12)
.borderRadius(12)
.backgroundColor(VG.card)
.alignItems(VerticalAlign.Center)
.onClick(() => {
this.selName = g.name
this.showDetail = true
})
}, (g: VgGlass) => g.name)
}
.width('100%')
.padding({ left: 14, right: 14 })
.margin({ top: 10 })
Text('驻坊匠人')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 18 })
Scroll() {
Row({ space: 12 }) {
ForEach(VG_WORKS, (wk: VgWork, i: number) => {
Column({ space: 6 }) {
Column() {
Text(wk.style)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
}
.width(96)
.height(96)
.borderRadius(14)
.backgroundColor(vgStyleColor(wk.style))
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.translate({ y: vgFloatY(this.wave, i) })
Text(wk.name)
.fontSize(11)
.fontColor(VG.text)
.fontWeight(FontWeight.Medium)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(wk.size + ' · ¥' + wk.price)
.fontSize(10)
.fontColor(VG.sub)
}
.width(100)
}, (wk: VgWork) => wk.name)
}
}
.scrollable(ScrollDirection.Horizontal)
.width('100%')
.margin({ top: 10, left: 14 })
.scrollBar(BarState.Off)
Column()
.width('100%')
.height(20)
}
.width('100%')
}
.scrollBar(BarState.Off)
.width('100%')
.height('100%')
.align(Alignment.Top)
.edgeEffect(EdgeEffect.Spring)
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Row({ space: 12 }) {
Column()
.width(56)
.height(56)
.borderRadius(12)
.backgroundColor(vgGlassColor(vgGlassByName(this.selName).colorName))
Column() {
Text(vgGlassByName(this.selName).name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text(vgGlassByName(this.selName).tag + ' · ' + vgGlassByName(this.selName).texture + '纹')
.fontSize(11)
.fontColor(VG.sub)
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥' + vgGlassByName(this.selName).price + '/片')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG_RUBY)
}
.width('100%')
Column({ space: 8 }) {
Text('透光率 ' + vgGlassByName(this.selName).trans + '%')
.fontSize(11)
.fontColor(VG.sub)
.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.borderRadius(4)
.backgroundColor(VG.bg)
Row()
.width(vgTransW(vgGlassByName(this.selName).trans) + '%')
.height(8)
.borderRadius(4)
.backgroundColor(VG_AMBER)
}
.width('100%')
Text('厚度 ' + vgGlassByName(this.selName).thickness + 'mm')
.fontSize(11)
.fontColor(VG.sub)
.width('100%')
.margin({ top: 6 })
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.borderRadius(4)
.backgroundColor(VG.bg)
Row()
.width(vgGlassByName(this.selName).thickness * 18 + '%')
.height(8)
.borderRadius(4)
.backgroundColor(VG_COBALT)
}
.width('100%')
}
.width('100%')
.margin({ top: 16 })
.padding(12)
.borderRadius(12)
.backgroundColor(VG.bg)
Row({ space: 8 }) {
Text('库存 ' + vgGlassByName(this.selName).stock + ' 片')
.fontSize(11)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 8, bottom: 8 })
.borderRadius(10)
.backgroundColor(VG.bg)
Text('已售 ' + vgGlassByName(this.selName).sold)
.fontSize(11)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 8, bottom: 8 })
.borderRadius(10)
.backgroundColor(VG.bg)
}
.width('100%')
.margin({ top: 10 })
Row({ space: 10 }) {
Text('关闭')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showDetail = false
})
Text('加入定制单')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showDetail = false
})
}
.width('100%')
.margin({ top: 16 })
}
.width('84%')
.constraintSize({ maxHeight: '70%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
}
}
@Component
struct VgGlassTab {
@State wave: number = 0
@State timer: number = -1
@State filterKey: string = '全部'
@State items: VgGlass[] = []
@State showDetail: boolean = false
@State showAdd: boolean = false
@State showEdit: boolean = false
@State showDel: boolean = false
@State selName: string = ''
@State inputName: string = ''
@State inputColor: string = ''
@State inputPrice: string = ''
@State editPrice: string = ''
aboutToAppear() {
this.items = VG_GLASS.slice()
this.timer = setInterval(() => {
this.wave = this.wave + 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder
modalOverlay() {
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(36,30,62,0.62)')
.position({ x: 0, y: 0 })
.zIndex(999)
.onClick(() => {
this.showDetail = false
this.showAdd = false
this.showEdit = false
this.showDel = false
})
}
build() {
Stack() {
Scroll() {
Column() {
Row({ space: 8 }) {
Text('玻璃仓库')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.layoutWeight(1)
Text('共 ' + vgFilterGlass(this.filterKey).length + ' 款')
.fontSize(11)
.fontColor(VG.sub)
}
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 12 })
Scroll() {
Row({ space: 8 }) {
ForEach(['全部', '热销', '进口', '经典', '新品', '手工'], (k: string) => {
Text(k)
.fontSize(12)
.fontColor(this.filterKey === k ? '#FFFFFF' : VG.sub)
.padding({ left: 16, right: 16, top: 7, bottom: 7 })
.borderRadius(16)
.backgroundColor(this.filterKey === k ? VG.primary : VG.card)
.onClick(() => {
this.filterKey = k
})
}, (k: string) => k)
}
}
.scrollable(ScrollDirection.Horizontal)
.width('100%')
.margin({ top: 10, left: 16 })
.scrollBar(BarState.Off)
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
ForEach(vgFilterGlass(this.filterKey), (g: VgGlass, i: number) => {
Column() {
Column() {
Row({ space: 4 }) {
Column()
.width(22)
.height(22)
.borderRadius(6)
.backgroundColor('#FFFFFF')
.opacity(0.5)
Column()
.width(14)
.height(14)
.borderRadius(4)
.backgroundColor('#FFFFFF')
.opacity(0.7)
}
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
Text(g.name)
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.margin({ top: 6 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.height(86)
.borderRadius(12)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor(vgGlassColor(g.colorName))
.opacity(vgShine(this.wave) === 0 ? 1 : 0.92)
Column() {
Text(g.tag + ' · ' + g.texture + '纹 · ' + g.thickness + 'mm')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 8 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row() {
Text('¥' + g.price)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(VG_RUBY)
Text('已售' + g.sold)
.fontSize(10)
.fontColor(VG.sub)
.margin({ left: 8 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.margin({ top: 6 })
Row({ space: 6 }) {
Text('详情')
.fontSize(10)
.fontColor(VG.primary)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(10)
.backgroundColor(VG.bg)
.onClick(() => {
this.selName = g.name
this.showDetail = true
})
Text('编辑')
.fontSize(10)
.fontColor(VG_AMBER)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(10)
.backgroundColor(VG.bg)
.onClick(() => {
this.selName = g.name
this.editPrice = vgGlassByName(this.selName).price.toString()
this.showEdit = true
})
Text('删除')
.fontSize(10)
.fontColor(VG_RUBY)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(10)
.backgroundColor(VG.bg)
.onClick(() => {
this.selName = g.name
this.showDel = true
})
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
.width('48.5%')
.padding(10)
.borderRadius(14)
.backgroundColor(VG.card)
.margin({ top: 10 })
}, (g: VgGlass) => g.name + this.wave)
}
.width('100%')
.padding({ left: 14, right: 14 })
.margin({ top: 4 })
Text('✚ 上架新玻璃')
.fontSize(13)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG.primary)
.margin({ left: 14, right: 14, top: 14 })
.onClick(() => {
this.showAdd = true
})
Text('透光率对比(%)')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 20 })
Column({ space: 8 }) {
ForEach(vgSoldRank(), (g: VgGlass) => {
Column({ space: 5 }) {
Row() {
Text(g.name)
.fontSize(11)
.fontColor(VG.text)
.layoutWeight(1)
Text(g.trans + '%')
.fontSize(11)
.fontColor(VG.sub)
}
.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.borderRadius(4)
.backgroundColor(VG.bg)
Row()
.width(vgTransW(g.trans) + '%')
.height(8)
.borderRadius(4)
.backgroundColor(vgGlassColor(g.colorName))
}
.width('100%')
}
.width('100%')
}, (g: VgGlass) => g.name)
}
.width('100%')
.padding(14)
.borderRadius(14)
.backgroundColor(VG.card)
.margin({ top: 10, left: 14, right: 14 })
Text('厚度分布(mm)')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 20 })
Row({ space: 14 }) {
ForEach(VG_GLASS, (g: VgGlass, i: number) => {
Column({ space: 6 }) {
Text(g.thickness + '')
.fontSize(10)
.fontColor(VG.sub)
Column()
.width(16)
.height(vgThickH(g.thickness))
.borderRadius(6)
.backgroundColor(vgGlassColor(g.colorName))
}
.alignItems(HorizontalAlign.Center)
}, (g: VgGlass) => g.name + g.thickness)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Bottom)
.padding({ top: 20, bottom: 10 })
.borderRadius(14)
.backgroundColor(VG.card)
.margin({ top: 10, left: 14, right: 14 })
Column()
.width('100%')
.height(20)
}
.width('100%')
}
.scrollBar(BarState.Off)
.width('100%')
.height('100%')
.align(Alignment.Top)
.edgeEffect(EdgeEffect.Spring)
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Row({ space: 12 }) {
Column()
.width(54)
.height(54)
.borderRadius(12)
.backgroundColor(vgGlassColor(vgGlassByName(this.selName).colorName))
Column() {
Text(vgGlassByName(this.selName).name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text(vgGlassByName(this.selName).colorName + ' · ' + vgGlassByName(this.selName).texture + '纹')
.fontSize(11)
.fontColor(VG.sub)
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('¥' + vgGlassByName(this.selName).price)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG_RUBY)
}
.width('100%')
Row({ space: 8 }) {
Column() {
Text(vgGlassByName(this.selName).thickness + 'mm')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG.primary)
Text('厚度')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
Column() {
Text(vgGlassByName(this.selName).trans + '%')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG_AMBER)
Text('透光率')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
Column() {
Text(vgGlassByName(this.selName).stock + '')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG_EMERALD)
Text('库存片数')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.margin({ top: 14 })
Column({ space: 6 }) {
Text('透光率进度')
.fontSize(11)
.fontColor(VG.sub)
.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.borderRadius(4)
.backgroundColor(VG.bg)
Row()
.width(vgTransW(vgGlassByName(this.selName).trans) + '%')
.height(8)
.borderRadius(4)
.backgroundColor(VG_AMBER)
}
.width('100%')
}
.width('100%')
.margin({ top: 12 })
.padding(12)
.borderRadius(12)
.backgroundColor(VG.bg)
Text('关闭')
.fontSize(14)
.fontColor('#FFFFFF')
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.margin({ top: 16 })
.onClick(() => {
this.showDetail = false
})
}
.width('84%')
.constraintSize({ maxHeight: '70%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showAdd) {
Stack() {
this.modalOverlay()
Column() {
Text('上架新玻璃')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('录入玻璃仓库信息')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 12 }) {
Column() {
Text('玻璃名称').fontSize(12).fontColor(VG.text).width('100%')
TextInput({ placeholder: '如:暮云灰玻璃' })
.fontSize(13)
.height(40)
.backgroundColor(VG.bg)
.borderRadius(10)
.margin({ top: 6 })
.onChange((v: string) => {
this.inputName = v
})
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('色系').fontSize(12).fontColor(VG.text).width('100%')
Row({ space: 8 }) {
TextInput({ placeholder: '如:钴蓝' })
.fontSize(13)
.height(40)
.backgroundColor(VG.bg)
.borderRadius(10)
.layoutWeight(1)
.margin({ top: 6 })
.onChange((v: string) => {
this.inputColor = v
})
Column()
.width(36)
.height(36)
.borderRadius(10)
.backgroundColor(vgGlassColor(this.inputColor))
.margin({ top: 6 })
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('单片价格(¥)').fontSize(12).fontColor(VG.text).width('100%')
TextInput({ placeholder: '如:48' })
.fontSize(13)
.height(40)
.backgroundColor(VG.bg)
.borderRadius(10)
.margin({ top: 6 })
.onChange((v: string) => {
this.inputPrice = v
})
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Row({ space: 10 }) {
Text('取消')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showAdd = false
})
Text('确认上架')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showAdd = false
})
}
.width('100%')
.margin({ top: 8 })
}
.width('100%')
.margin({ top: 16 })
}
.width('84%')
.constraintSize({ maxHeight: '78%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showEdit) {
Stack() {
this.modalOverlay()
Column() {
Text('编辑玻璃信息')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Row({ space: 10 }) {
Column()
.width(40)
.height(40)
.borderRadius(10)
.backgroundColor(vgGlassColor(vgGlassByName(this.selName).colorName))
Column() {
Text(vgGlassByName(this.selName).name)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor(VG.text)
Text('当前价 ¥' + vgGlassByName(this.selName).price + ' / 库存 ' + vgGlassByName(this.selName).stock)
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 3 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.width('100%')
.margin({ top: 14 })
.alignItems(VerticalAlign.Center)
Column() {
Text('调整价格(¥)').fontSize(12).fontColor(VG.text).width('100%')
TextInput({ text: this.editPrice })
.fontSize(13)
.height(40)
.backgroundColor(VG.bg)
.borderRadius(10)
.margin({ top: 6 })
.onChange((v: string) => {
this.editPrice = v
})
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 12 })
Row({ space: 8 }) {
Text('补货 +20')
.fontSize(12)
.fontColor(VG.primary)
.padding({ left: 14, right: 14, top: 8, bottom: 8 })
.borderRadius(16)
.backgroundColor(VG.bg)
Text('促销 -¥5')
.fontSize(12)
.fontColor(VG_AMBER)
.padding({ left: 14, right: 14, top: 8, bottom: 8 })
.borderRadius(16)
.backgroundColor(VG.bg)
}
.margin({ top: 12 })
Row({ space: 10 }) {
Text('取消')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showEdit = false
})
Text('保存修改')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showEdit = false
})
}
.width('100%')
.margin({ top: 18 })
}
.width('84%')
.constraintSize({ maxHeight: '70%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showDel) {
Stack() {
this.modalOverlay()
Column() {
Column() {
Text('⚠').fontSize(30).fontColor(VG_RUBY)
}
.width(56)
.height(56)
.borderRadius(28)
.backgroundColor('rgba(190,18,60,0.1)')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('确认下架该玻璃?')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.margin({ top: 12 })
Text('「' + this.selName + '」将从仓库移除')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Text('已售出的订单不受影响')
.fontSize(11)
.fontColor(VG.sub)
.margin({ top: 2 })
Row({ space: 10 }) {
Text('再想想')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showDel = false
})
Text('确认下架')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG_RUBY)
.onClick(() => {
this.showDel = false
})
}
.width('100%')
.margin({ top: 18 })
}
.width('76%')
.constraintSize({ maxHeight: '60%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.alignItems(HorizontalAlign.Center)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
}
}
@Component
struct VgWorkTab {
@State wave: number = 0
@State timer: number = -1
@State showDetail: boolean = false
@State showAdd: boolean = false
@State showDel: boolean = false
@State selName: string = ''
@State inputName: string = ''
@State inputStyle: string = '哥特'
aboutToAppear() {
this.timer = setInterval(() => {
this.wave = this.wave + 1
}, 60)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
}
}
@Builder
modalOverlay() {
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(36,30,62,0.62)')
.position({ x: 0, y: 0 })
.zIndex(999)
.onClick(() => {
this.showDetail = false
this.showAdd = false
this.showDel = false
})
}
build() {
Stack() {
Scroll() {
Column() {
Row() {
Text('匠人作品')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.layoutWeight(1)
Text('每件孤品')
.fontSize(11)
.fontColor(VG.sub)
}
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 12 })
Scroll() {
Row({ space: 12 }) {
ForEach(VG_WORKS, (wk: VgWork, i: number) => {
Column() {
Column() {
Text(wk.style)
.fontSize(13)
.fontColor('#FFFFFF')
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.borderRadius(10)
.backgroundColor('rgba(255,255,255,0.25)')
Row({ space: 3 }) {
Column().width(16).height(16).borderRadius(4).backgroundColor('rgba(255,255,255,0.35)').rotate({ angle: 45 })
Column().width(16).height(16).borderRadius(4).backgroundColor('rgba(255,255,255,0.55)').rotate({ angle: 45 })
Column().width(16).height(16).borderRadius(4).backgroundColor('rgba(255,255,255,0.75)').rotate({ angle: 45 })
}
.margin({ top: 10 })
.translate({ y: vgFloatY(this.wave, i) })
Text('¥' + wk.price)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.margin({ top: 10 })
}
.width(160)
.height(130)
.borderRadius(14)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor(vgStyleColor(wk.style))
Column() {
Text(wk.name)
.fontSize(12)
.fontWeight(FontWeight.Medium)
.fontColor(VG.text)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 8 })
Text(wk.size + ' · ' + wk.panels + ' 块 · 已售 ' + wk.sold)
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 3 })
}
.width(160)
.alignItems(HorizontalAlign.Start)
}
.width(160)
.padding(10)
.borderRadius(16)
.backgroundColor(VG.card)
.onClick(() => {
this.selName = wk.name
this.showDetail = true
})
}, (wk: VgWork) => wk.name)
}
}
.scrollable(ScrollDirection.Horizontal)
.width('100%')
.margin({ top: 10, left: 14 })
.scrollBar(BarState.Off)
Text('✚ 发布新作品')
.fontSize(13)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 12, bottom: 12 })
.borderRadius(14)
.backgroundColor(VG_RUBY)
.margin({ left: 14, right: 14, top: 14 })
.onClick(() => {
this.showAdd = true
})
Text('风格分布')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 20 })
Column({ space: 10 }) {
ForEach(['哥特', '蒂芙尼', '现代', '新艺术', '拜占庭'], (s: string) => {
Row({ space: 10 }) {
Column()
.width(10)
.height(10)
.borderRadius(3)
.backgroundColor(vgStyleColor(s))
Text(s)
.fontSize(12)
.fontColor(VG.text)
.width(64)
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(10)
.borderRadius(5)
.backgroundColor(VG.bg)
Row()
.width(vgStyleCount(s) * 30 + '%')
.height(10)
.borderRadius(5)
.backgroundColor(vgStyleColor(s))
}
.layoutWeight(1)
}
.width('100%')
.alignItems(VerticalAlign.Center)
}, (s: string) => s)
}
.width('100%')
.padding(14)
.borderRadius(14)
.backgroundColor(VG.card)
.margin({ top: 10, left: 14, right: 14 })
Text('拼块数对比(块/件)')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.width('100%')
.padding({ left: 16, right: 16 })
.margin({ top: 20 })
Row({ space: 10 }) {
ForEach(VG_WORKS, (wk: VgWork) => {
Column({ space: 6 }) {
Text(wk.panels + '')
.fontSize(10)
.fontColor(VG.sub)
Column()
.width(18)
.height(vgPanelW(wk.panels))
.borderRadius(6)
.backgroundColor(vgStyleColor(wk.style))
Text(wk.style)
.fontSize(8)
.fontColor(VG.sub)
.maxLines(1)
}
.alignItems(HorizontalAlign.Center)
}, (wk: VgWork) => wk.name + wk.panels)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Bottom)
.padding({ top: 20, bottom: 10 })
.borderRadius(14)
.backgroundColor(VG.card)
.margin({ top: 10, left: 14, right: 14 })
Column()
.width('100%')
.height(20)
}
.width('100%')
}
.scrollBar(BarState.Off)
.width('100%')
.height('100%')
.align(Alignment.Top)
.edgeEffect(EdgeEffect.Spring)
if (this.showDetail) {
Stack() {
this.modalOverlay()
Column() {
Column() {
Text(vgWorkByName(this.selName).style)
.fontSize(13)
.fontColor('#FFFFFF')
.padding({ left: 12, right: 12, top: 5, bottom: 5 })
.borderRadius(12)
.backgroundColor('rgba(255,255,255,0.3)')
Text(vgWorkByName(this.selName).name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.margin({ top: 8 })
Text('¥' + vgWorkByName(this.selName).price + ' · ' + vgWorkByName(this.selName).size)
.fontSize(12)
.fontColor('rgba(255,255,255,0.85)')
.margin({ top: 6 })
}
.width('100%')
.padding({ top: 22, bottom: 22 })
.borderRadius(14)
.backgroundColor(vgStyleColor(vgWorkByName(this.selName).style))
.alignItems(HorizontalAlign.Center)
Row({ space: 8 }) {
Column() {
Text(vgWorkByName(this.selName).panels + '')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.primary)
Text('玻璃拼块')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
Column() {
Text(vgWorkByName(this.selName).colorCount + '')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG_AMBER)
Text('用色数')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
Column() {
Text(vgWorkByName(this.selName).sold + '')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG_EMERALD)
Text('累计售出')
.fontSize(10)
.fontColor(VG.sub)
.margin({ top: 2 })
}
.layoutWeight(1)
.padding({ top: 10, bottom: 10 })
.borderRadius(12)
.backgroundColor(VG.bg)
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.margin({ top: 12 })
Column({ space: 6 }) {
Text('拼块规模')
.fontSize(11)
.fontColor(VG.sub)
.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row()
.width('100%')
.height(8)
.borderRadius(4)
.backgroundColor(VG.bg)
Row()
.width(vgPanelW(vgWorkByName(this.selName).panels) + '%')
.height(8)
.borderRadius(4)
.backgroundColor(vgStyleColor(vgWorkByName(this.selName).style))
}
.width('100%')
}
.width('100%')
.margin({ top: 12 })
.padding(12)
.borderRadius(12)
.backgroundColor(VG.bg)
Row({ space: 10 }) {
Text('删除作品')
.fontSize(13)
.fontColor(VG_RUBY)
.padding({ left: 14, right: 14, top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor('rgba(190,18,60,0.08)')
.onClick(() => {
this.showDetail = false
this.showDel = true
})
Text('立即定制')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.primary)
.onClick(() => {
this.showDetail = false
})
}
.width('100%')
.margin({ top: 16 })
}
.width('86%')
.constraintSize({ maxHeight: '75%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showAdd) {
Stack() {
this.modalOverlay()
Column() {
Text('发布新作品')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
Text('登记你的花窗 / 灯罩作品')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Column({ space: 12 }) {
Column() {
Text('作品名称').fontSize(12).fontColor(VG.text).width('100%')
TextInput({ placeholder: '如:黄昏落日横窗' })
.fontSize(13)
.height(40)
.backgroundColor(VG.bg)
.borderRadius(10)
.margin({ top: 6 })
.onChange((v: string) => {
this.inputName = v
})
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('作品风格').fontSize(12).fontColor(VG.text).width('100%')
Row({ space: 8 }) {
ForEach(['哥特', '蒂芙尼', '现代', '新艺术', '拜占庭'], (s: string) => {
Text(s)
.fontSize(12)
.fontColor(this.inputStyle === s ? '#FFFFFF' : VG.sub)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.borderRadius(12)
.backgroundColor(this.inputStyle === s ? vgStyleColor(s) : VG.bg)
.onClick(() => {
this.inputStyle = s
})
}, (s: string) => s + this.inputStyle)
}
.margin({ top: 8 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
Row({ space: 10 }) {
Text('取消')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showAdd = false
})
Text('提交审核')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG_RUBY)
.onClick(() => {
this.showAdd = false
})
}
.width('100%')
.margin({ top: 8 })
}
.width('100%')
.margin({ top: 16 })
}
.width('84%')
.constraintSize({ maxHeight: '72%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
if (this.showDel) {
Stack() {
this.modalOverlay()
Column() {
Column() {
Text('✕').fontSize(24).fontColor(VG_RUBY)
}
.width(52)
.height(52)
.borderRadius(26)
.backgroundColor('rgba(190,18,60,0.1)')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
Text('删除这件作品?')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(VG.text)
.margin({ top: 12 })
Text('「' + this.selName + '」将不可恢复')
.fontSize(12)
.fontColor(VG.sub)
.margin({ top: 6 })
Text('已下单客户的制作不受影响')
.fontSize(11)
.fontColor(VG.sub)
.margin({ top: 2 })
Row({ space: 10 }) {
Text('取消')
.fontSize(14)
.fontColor(VG.sub)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG.bg)
.onClick(() => {
this.showDel = false
})
Text('删除')
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(20)
.backgroundColor(VG_RUBY)
.onClick(() => {
this.showDel = false
})
}
.width('100%')
.margin({ top: 18 })
}
.width('76%')
.constraintSize({ maxHeight: '60%' })
.borderRadius(16)
.backgroundColor(VG.card)
.padding(20)
.alignItems(HorizontalAlign.Center)
.zIndex(1000)
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
}
}
@Component
struct VgToolTab {
@State wave: number = 0
@State timer: number = -1
@State showDetail: boolean = false
@State showAdd: boolean = false
@State showEdit: boolean = false
@State selName: string = ''
}
总结

这款彩绘玻璃艺术工坊应用是四款应用中功能最丰富、技术最有特色的一款。它不仅拥有完整的电商功能(玻璃材料、成品作品、工具耗材),还创新性地引入了课程培训体系,形成了"材料+作品+工具+课程"的一站式服务闭环,为彩绘玻璃爱好者提供了全方位的学习和创作支持。
多色映射系统是应用最具特色的技术亮点。vgGlassColor和vgStyleColor两个颜色映射函数,将业务语义(颜色名称、风格名称)与视觉表现(十六进制颜色值)优雅地解耦,既提升了代码的可维护性,又为数据可视化提供了丰富的色彩表现。彩色进度条、风格分布图等特色图表,都是基于这一技术实现的。
更多推荐



所有评论(0)