后台搭好了,客户催着要移动端。一个人,两周时间,一套代码覆盖 App + 小程序 + H5——从脚手架到第一个业务页面的全过程复盘。


一、为什么选 uni-app

方案 开发成本 覆盖平台 维护
原生(Flutter/RN) iOS + Android
微信小程序原生 仅微信
uni-app + Vue3 App+小程序+H5+鸿蒙

核心原因:后台是 Vue3,移动端也用 Vue3,技术栈完全一致,心智负担为零。


二、项目结构与路由

1
2
3
4
5
6
7
8
pages/
├── index/ # 工作台
├── customer/ # 客户列表 + 详情 + 编辑
├── visit/ # 拜访记录
└── mine/ # 个人中心
api/
├── request.js # 请求封装(含 Token 管理)
└── modules/ # 按模块拆分接口

路由用 pages.json 配置 TabBar,页面跳转用 uni.navigateTo / uni.switchTab,不用 Vue Router。


三、请求封装(核心)

uni-app 不能用 axios,必须封装 uni.request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const request = (options) => {
return new Promise((resolve, reject) => {
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data,
header: {
'Authorization': `Bearer ${getToken()}`
},
success: (res) => {
const { code, data, msg } = res.data
if (code === 200) resolve(data)
else if (code === 401) {
// Token 过期 → 清空 → 跳登录
uni.removeStorageSync('token')
uni.reLaunch({ url: '/pages/login/index' })
}
}
})
})
}

Token 存储必须用条件编译:H5 用 localStorage,App/小程序用 uni.getStorageSync,混用直接报错。


四、客户列表(完整示例)

搜索 + 卡片列表 + 上拉加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script setup>
import { ref } from 'vue'
import { onReachBottom } from '@dcloudio/uni-app'

const list = ref([])
const page = ref(1)
const loadStatus = ref('more')

const fetchData = async (reset = false) => {
if (reset) page.value = 1
const { rows, total } = await listCustomer({ pageNum: page.value, pageSize: 10, name: keyword.value })
list.value = reset ? rows : [...list.value, ...rows]
loadStatus.value = list.value.length >= total ? 'noMore' : 'more'
}

onReachBottom(() => {
if (loadStatus.value !== 'more') return
page.value++
fetchData()
})
</script>

五、三个最常踩的坑

现象 原因 解决
API 不兼容 H5 白屏 用了 uni.getStorageSync 但 H5 限制 条件编译 #ifdef H5 隔离
页面栈溢出 跳不动了 反复 navigateTo 超 10 层 详情→编辑用 redirectTo
表单校验失效 提交不校验 name 和 rules 不匹配 核对 name 一致性

六、当前覆盖与后续

四个 Tab:工作台 / 客户 / 拜访 / 我的,对接后端约 20 个接口。后续会增加商机管理和工单模块。

下一篇:Pinia 模块化状态管理 + Token 刷新机制。