尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

uni-app仿支付宝UI实战:跨端组件封装与交互细节解析

uni-app仿支付宝UI实战:跨端组件封装与交互细节解析 简介一份基于Uni-app与Sumer UI 3.0组件库打造的支付宝风格UI前端源码适合移动端开发者、UI工程师及跨平台应用学习者借鉴。项目完整模拟支付宝首页、理财、消息、生活、口碑、我的六大核心页面并集成扫码支付、收付款、蚂蚁森林种树养鸡、农场偷菜等交互流程高度还原官方视觉与动效。资源包共3个文件以inscode工程文件为主体另含html预览页面和gitignore配置文件整体仅8KB需配合Hbuilder导入运行源码未加密目录含components、pages、static等标准结构注释清晰便于二次开发。当前已有94人学习适合以此为基础快速搭建电商、生活服务、金融类应用的UI原型并了解Uni-app多端适配与状态管理实践。 “uniapp精仿支付宝UI界面[可运行源码]”这个标题我第一眼看到就觉得是个值得拆解的练手项目。支付宝的 UI 复杂度在互联网应用里属于第一梯队密集的首页金刚区、多层级的蜂窝布局、支付密码键盘、账单筛选、资产曲线几乎每一个模块都考验组件封装和交互设计。而 uni-app 又能把这套 UI 跑到微信小程序、支付宝小程序、H5 和 App 上这种“一套代码多端复用”的特性正好用来验证一个前端团队的组件抽象能力。这个项目适合谁一是刚学完 uni-app 基础、想找个综合案例练手的人二是在做金融类/工具类应用、需要参考成熟交互方案的前端工程师三是想研究跨端项目中“UI 还原度”和“小程序/App 适配”差异的人。源码既然标注可运行说明不是截图或静态原型你 clone 下来装上依赖就能看到真实页面效果。这篇文章我会从整体设计、核心细节、实操过程、问题排查四个维度展开把我自己复刻这套 UI 时踩过的坑和总结的经验一起写出来。1. 项目整体设计与思路拆解1.1 为什么选择 uni-app 来复刻支付宝 UI支付宝 App 本身是原生技术栈但我们做前端技术研究和作品集时完全没必要用 Swift/Kotlin 各写一遍。uniapp 最典型的应用场景就是“一次开发多端发布”尤其在国内微信小程序和支付宝小程序的运行时差异很大如果直接用原生小程序语法写维护两套代码的成本是翻倍的。用 uni-app 做这套仿写页面结构、样式、逻辑全都可以共用出成品和后续改版都会快很多。另一个现实原因是招聘和面试。现在很多公司问 uni-app 相关问题比如“uniapp 怎么打包”“manifest 配置注意什么”“组件生命周期”等如果你拿这个仿支付宝项目做例子回答会显得非常有说服力。项目里涉及的 API 封装、自定义组件、多端兼容处理都是面试里常考的点。1.2 支付宝 UI 的核心元素与可拆解性复刻一个成熟 App 的 UI不能上来就照抄截图得先拆层。支付宝给人的“精致感”主要来自三块信息密度的节奏控制、统一但灵活的圆角体系、以及弱阴影层级。比如首页金刚区是密集矩阵但下方“生活服务/便民服务”又会变成两列卡片形成疏密对比资产页的曲线图用了很浅的渐变底色让数字成为视觉重心。从组件角度看核心模块可以拆成这些首页金刚区九/十格的 icon 宫格需要支持长按编辑。财富资产卡片状态切换、收益率数据、趋势变化。账单列表右侧金额红绿变化、左侧 icon 状态、顶部时间筛选。支付密码输入框6 位隐藏/明文切换、自定义数字键盘。底部 tabBar首页、财富、口碑、朋友、我的五个一级入口。把这些模块拆出来之后再组合代码结构会非常干净也方便后续按需替换数据源。项目标题里强调“可运行源码”意味着这几个模块必须是真的能交互不是静态页面。所以我在实操时重点把支付密码弹窗、账单分类筛选、首页模块 grid 做了动态渲染。1.3 技术选型背后的取舍uni-app 的 UI 适配基础是 rpx它把 750rpx 对应到屏幕宽度设计稿宽度 750px 时直接把像素写为 rpx 即可。这个机制和微信小程序的 rpx 一致在复刻支付宝 UI 时可以直接按设计稿 1:1 写但要小心 iPhone 屏幕刘海屏和安全区。支付宝首页的顶部导航栏高度不是固定值而是“状态栏高度 44px”这部分在 uni-app 里要用uni.getSystemInfoSync().statusBarHeight动态计算。另一个取舍是组件库。市面上有 uView、thorui、uni-ui 等组件库直接引入能省不少事但“精仿”的场景下组件库的默认样式很难完全贴合支付宝的设计语言。我的建议是基础布局和工具方法可以用组件库和 uni-app 内置 API但核心的 UI 组件支付密码、数字键盘、通用弹窗最好自己封装。这也是我做这个项目最大的收获——亲手写一遍组件比改配置理解深得多。2. 核心细节解析与实操要点2.1 自定义底部 tabBar 的实现方案支付宝的 tabBar 其实非常克制选中态是蓝黑色图标加文字没有中间凸起也没有复杂的动画。用 uni-app 内置tabBar配置可以快速实现但内置 tabBar 的问题在于图标只能是 static 目录下的图片无法直接用 iconfont 字体而且不同机型 tabBar 高度和安全区适配不如自定义灵活。我最终采用的方式是“页面内自定义 tabBar 页面路由切换”。第一步在pages.json里把tabBar配置移除只配置五个首页级页面为普通页面。 第二步创建一个CustomTabBar.vue组件使用cover-view还是view这里注意如果在 App 平台普通view足够如果要在小程序里覆盖原生地图/视频组件才需要cover-view。我们这个 UI 项目不涉及地图所以用view就好。 第三步在五个页面中分别引入这个组件传current表示当前选中项再通过uni.switchTab跳转。自定义 tabBar 的样式讲究高度固定 50px再加padding-bottom: env(safe-area-inset-bottom)适配底部小黑条。图标字体放iconfont文件选中态切换颜色。点击时按钮需要hover-class反馈否则看起来像死 UI。这里有个小坑如果用自定义 tabBar首次进入子页面再返回时页面原生导航栏和容器区域的交互容易分层。建议给外层容器加padding-bottom: 50px safe-area防止页面内容被遮挡。2.2 支付密码输入框和数字键盘这个组件是支付宝 UI 里比较有代表性的交互。用户点击“支付”后底部会弹出数字键盘同时页面中间出现一个 6 宫格密码框每输入一位数字宫格的圆点会亮起密码达到 6 位后自动关闭并回调。实现思路密码框是 6 个独立的小容器使用 CSS 的border-radius: 4rpx、左右border-right形成格子。真正用于接收输入的input隐藏在最底下设password为 false宽度设为 1px透明度为 0。点击密码框区域时调用this.$refs.hiddenInput.focus()聚焦隐藏输入框。键盘组件是自定义的 1-9、0、删除/关闭 布局点击时通过uni.$emit或者直接绑定事件把数字传给父组件。当value.length 6时emitconfirm事件执行支付模拟逻辑。我在测试中发现隐藏 input 在 H5 端会自动弹出系统键盘干扰自定义键盘。解决方案是给 input 加disabled但这样又没法聚焦。更稳的做法是在 H5 端直接捕获keydown事件或者用focusfalse 动态设置focus属性控制。这个细节虽然小但很影响体验。2.3 首页金刚区与长按编辑支付宝金刚区的 icon 数量是可变的常规状态下是 11 个4 行左对齐点右上角“更多”会出现完整布局。复刻时要把数据源交给 JS 管理格式类似[ { id: 1, name: 转账, icon: icon-zhuanzhang, bg: #1677FF, link: /pages/transfer/index }, { id: 2, name: 信用卡还款, icon: icon-ka, bg: #FF8F1F, link: /pages/repayment/index } ]页面里用v-for渲染 grid配合uni-app的rr-grid或手写 flex 布局都行。我建议自定义布局而不是套组件库因为支付宝的网格间距是“大间距小圆角”组件库默认间距往往偏紧。长按编辑如果需要做可用longpress事件触发编辑态每个 icon 右上角出现减号按钮点击后删掉对应 item。这个状态需要派发到全局 Store比如 Pinia/Vuex否则多个页面同步会乱。3. 实操过程与核心环节实现3.1 从零搭建项目基础骨架我用 HBuilderX 创建了uni-app项目Vue 3 版本。如果团队已经在用 Vue2 也没有问题但建议新项目直接 Vue3 Vite性能更好并且 uni-app 官方对 Vue3 的支持已经成熟。基础配置分三块manifest.json配置 App 名称、应用图标、启动图。在“App 模块配置”里勾选需要的模块比如 Payment 支付模块、Geolocation 定位在“App 权限配置”里按需开启。还有一个容易被忽略的——app-plus节点里的bounce字段它控制 App 端页面下拉回弹效果复刻支付宝时如果不想让页面像普通网页一样回弹需要设为bounce: none。pages.json注册所有页面设置navigationStyle: custom这样每个页面可以使用自己的自定义导航栏看起来更贴近支付宝沉浸式效果。uni.scss全局引入设计变量比如主题色#1677FF、背景色#F5F6F7、字体大小等。创建完成后我把常用工具函数封装到utils/比如request.js封装uni.request统一拦截 tokenformat.js做金额格式化适配两位小数和千分位。虽然项目用 mock 数据但提前封装能方便后续接真实接口。3.2 首页和财富页面的实现演示首页仿写时我主要做了三块顶部搜索栏、金刚区、中间卡片流。顶部搜索栏是自定义导航栏动态高度 状态栏高度 44px左边是支付宝的“支付宝”三个字 logo中间是“搜索”输入框占位右侧是“消息”图标。实现时注意uni-app里的状态栏高度在 iPhone X 和普通机型上不同必须用uni.getSystemInfoSync()拿真实值不能写死。金刚区的核心是 grid 布局view classgrid view classgrid-item v-foritem in gridList :keyitem.id clickgoPage(item.link) view classicon-wrap :style{ backgroundColor: item.bg } text classiconfont :classitem.icon/text /view text classname{{ item.name }}/text /view /view样式上grid-item宽度用calc((750rpx - 3 * 20rpx) / 4)控制四列布局icon 和文字之间留 12rpx。支付宝的 icon 不是正方形的是圆角矩形背景加白色图标所以icon-wrap的宽高设置 96rpxborder-radius: 24rpx颜色和图标来自 JSON 数据。财富页我仿的是资产总额、昨日收益、累计收益这一块。页面顶部是一个大的资产卡片下方是近一个月的收益图表。图表如果不引入 ECharts可以用两层堆叠 flex 柱状图实现简单且无重依赖view classchart-column v-foritem in chartData :keyitem.month view classbar :style{ height: item.value * 2 rpx }/view text classlabel{{ item.month }}/text /view用 CSS 柱状图代替 canvas优点是渲染快、适配简单缺点是没法做复杂交互。对于仿支付宝 UI 的作品集项目视觉还原度足够。3.3 支付流程的模拟与回调逻辑支付是支付宝的核心场景仿写时可以做一个模拟支付流程用户点击“立即支付” - 弹出订单确认页 - 选择支付方式 - 弹出密码键盘 - 模拟支付成功并跳转结果页。这一步用到了uni.$emit和uni.$on做跨页面通信。比如在订单确认页点击“确认支付”后uni.$emit(showPaySheet, { amount: 199.00 });这个事件由全局挂载的PaySheet组件监听组件内部弹出底部抽屉显示金额和支付方式列表点击后调起数字键盘。密码输入完成后uni.showLoading模拟请求再uni.redirectTo到结果页。为了让它看起来更真实我加了 800ms 的 delay 来模拟网络延迟并且把支付方式选择做成了可切换的 radio 样式。如果有条件接真实支付可以用“支付宝沙箱支付”来联调这一步涉及配置应用的私钥、公钥、网关地址需要在开放平台的后台创建沙箱应用比较繁琐我建议先 mock。3.4 可运行源码的组织方式“可运行”不是简单地把页面堆在一起而是要让别人 clone 下来就能跑起来。我的目录结构会长这样├─ components │ ├─ CustomTabBar.vue │ ├─ PayKeyboard.vue │ ├─ PayPasswordBox.vue │ └─ AmountTrendChart.vue ├─ pages │ ├─ index/index.vue │ ├─ wealth/index.vue │ ├─ bill/index.vue │ ├─ messages/index.vue │ └─ mine/index.vue ├─ pages.json ├─ manifest.json ├─ static │ └─ iconfont/ ├─ utils │ ├─ request.js │ └─ format.js └─ store └─ app.js把 mock 数据放到store或独立的mock/目录中页面通过异步方法模拟请求export function fetchBillList(params) { return new Promise((resolve) { setTimeout(() { resolve(billMockData); }, 300); }); }这样后续要替换成真实接口时只需要改动utils/request.js和 API 函数即可页面逻辑不用重写。4. 常见问题与排查技巧实录4.1 下拉刷新与页面滚动冲突支付宝首页可以整页下拉刷新但页面内又有大量的滚动列表。uni-app 里如果页面配置了enablePullDownRefresh手指在页面顶部下拉时一般会触发页面刷新但如果你把内容区域放在一个scroll-view里就会发生“页面回弹刷新”和“scroll-view 滚动”同时触发的问题。我的解决办法是不要使用scroll-view让页面本身滚动然后监听onPageScroll判断滚动位置是否接近顶部。如果需要做“顶部吸顶”效果用position: sticky代替scroll-view这样下拉手势会先被页面拦截再触发刷新回调。另外在pages.json对应页面配置里设置{ enablePullDownRefresh: true, backgroundTextStyle: dark, onReachBottomDistance: 50 }这样实现起来最贴近支付宝的手势反馈。4.2 页面白屏和 not found: page这个问题我在调试时遇到过两次都是因为pages.json里的页面路径大小写或后缀不对。uni-app 的页面路径是基于项目根目录的不能写错前缀比如pages/Index/index和pages/index/index是不同的。另一个常见原因是某个页面里引入了不存在的全局组件或插件导致编译报错页面白屏。排查步骤看控制台是否有编译警告定位到具体文件行号。打开pages.json逐项检查路径是否和真实文件名一致。把自定义组件注释掉看是否恢复正常。如果是 App 端 HBuilderX 内置浏览器预览白屏改用真机预览因为 App 端某些 API 在 H5 端不支持。4.3 运行到支付宝小程序失败项目名虽然是支付宝 UI但不代表直接运行到支付宝小程序就能跑。支付宝小程序的运行时和微信小程序略有差异比如某些 CSS 属性不支持或者uni-app编译后的组件差异。我自己遇到的是iconfont在支付宝小程序里不显示原因是支付宝小程序的 base64 字体文件限制。解决方案将字体文件转为 Online URL 或使用本地static/iconfont文件并在font-face里声明相对路径。如果只是预览效果建议先用 H5 或微信小程序跑通再切到支付宝小程序。这个项目能跑通支付宝小程序也是加分的但不能作为第一优先级。4.4 组件生命周期与页面通信坑点支付宝 UI 有很多页面是同一个组件复用的比如账单列表的摘要卡片和账单详情页的详情卡片。如果组件在多个页面间切换要注意onShow和onHide与组件内部created的生命周期差异。比如账单页的筛选条件改变了需要通知列表组件重新拉数据。我用的是uni.$on(billFilterChange, callback)。但一定要在页面onUnload里uni.$off否则多个页面互相监听会触发很多次请求。这一点在长列表页面尤其明显。另一个坑组件props默认是静态值如果父组件传的是对象或数组子组件里直接修改 props 会报警告。我的做法是深拷贝后再操作const localList JSON.parse(JSON.stringify(this.list));4.5 打包上安卓应用市场的注意事项做完这个 UI 项目很多人会想发布。uni-app 打包成 Android App 有两种方式云打包和离线打包。云打包直接在 HBuilderX 中填入包名、证书指纹默认会提供 DCloud 公共证书但上应用市场需要自己生成证书。我建议提前用keytool生成.keystore文件配置好manifest.json中的app-plus节点distribute: { android: { permissions: [ uses-permission android:name\android.permission.INTERNET\/ ] } }上传应用市场前要特别注意不要用DCloud_uniCloud的默认 AppID 直接发布可能会遇到签名不一致问题。另外支付宝 UI 仿写项目如果用于展示最好在应用描述里写明“学习演示用途非官方应用”避免侵权风险。最后再分享一个小技巧如果你在自定义 tabBar 和页面跳转之间遇到“当前页面缓存导致 tabBar 状态不更新”的问题可以在每个页面的onShow里重新读取当前的 tab 索引并赋值给currentonShow() { this.currentTab 0; // 当前页面对应的索引 }这比在跳转路由时手动传参靠谱得多。这个小改动帮我解决了很多次页面返回后 tabBar 高亮错位的困扰。做这种精仿 UI 项目最忌讳的就是“能跑就行”多花一点时间把组件和通信机制理顺收获会比单纯复制源码大得多。本文还有配套的精品资源点击获取
返回列表