21 KiB
21 KiB
小程序开发指南
版本历史
| 版本 | 日期 | 修改人 | 修改内容 |
|---|---|---|---|
| 1.0.0 | 2024-01-20 | 开发团队 | 初始版本 |
1. 小程序矩阵概述
1.1 小程序架构
本项目采用多小程序架构,包含四个独立的小程序:
- 员工小程序 (staff-mp): 内部员工使用,管理订单、质检、运输等
- 司机小程序 (driver-mp): 运输司机使用,管理运输任务和状态更新
- 供应商小程序 (supplier-mp): 供应商使用,管理订单接收和牛只信息
- 客户小程序 (client-mp): 客户使用,下单、查看订单状态等
1.2 技术栈
- 框架: uni-app (Vue3 + TypeScript)
- UI组件: uView Plus
- 状态管理: Pinia
- HTTP请求: uni.request 封装
- 构建工具: Vite
2. 开发环境搭建
2.1 工具安装
# 安装 HBuilderX (推荐)
# 下载地址: https://www.dcloud.io/hbuilderx.html
# 或使用 VS Code + uni-app 插件
# 安装 uni-app 插件
2.2 微信开发者工具
# 下载微信开发者工具
# 下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
# 配置小程序 AppID
# 在各小程序的 manifest.json 中配置对应的 AppID
2.3 项目初始化
# 进入小程序目录
cd mini_program/staff-mp
# 安装依赖
npm install
# 启动开发服务器
npm run dev:mp-weixin
3. 项目结构详解
3.1 员工小程序 (staff-mp)
staff-mp/
├── src/
│ ├── pages/ # 页面
│ │ ├── index/ # 首页
│ │ ├── order/ # 订单管理
│ │ │ ├── list.vue # 订单列表
│ │ │ ├── detail.vue # 订单详情
│ │ │ └── create.vue # 创建订单
│ │ ├── quality/ # 质检管理
│ │ ├── transport/ # 运输管理
│ │ └── profile/ # 个人中心
│ ├── components/ # 组件
│ │ ├── OrderCard.vue # 订单卡片
│ │ ├── QualityForm.vue # 质检表单
│ │ └── StatusBadge.vue # 状态标签
│ ├── api/ # API接口
│ │ ├── order.ts # 订单接口
│ │ ├── quality.ts # 质检接口
│ │ └── transport.ts # 运输接口
│ ├── stores/ # 状态管理
│ │ ├── user.ts # 用户状态
│ │ ├── order.ts # 订单状态
│ │ └── app.ts # 应用状态
│ ├── utils/ # 工具函数
│ │ ├── request.ts # 请求封装
│ │ ├── auth.ts # 认证工具
│ │ └── format.ts # 格式化工具
│ ├── static/ # 静态资源
│ ├── App.vue # 应用入口
│ ├── main.ts # 主文件
│ └── pages.json # 页面配置
├── package.json
└── tsconfig.json
3.2 司机小程序 (driver-mp)
driver-mp/
├── src/
│ ├── pages/
│ │ ├── index/ # 首页
│ │ ├── task/ # 运输任务
│ │ │ ├── list.vue # 任务列表
│ │ │ ├── detail.vue # 任务详情
│ │ │ └── track.vue # 运输跟踪
│ │ ├── vehicle/ # 车辆管理
│ │ └── profile/ # 个人中心
│ └── ...
3.3 供应商小程序 (supplier-mp)
supplier-mp/
├── src/
│ ├── pages/
│ │ ├── index/ # 首页
│ │ ├── order/ # 订单管理
│ │ ├── cattle/ # 牛只管理
│ │ └── profile/ # 个人中心
│ └── ...
3.4 客户小程序 (client-mp)
client-mp/
├── src/
│ ├── pages/
│ │ ├── index/ # 首页
│ │ ├── order/ # 订单管理
│ │ ├── payment/ # 支付管理
│ │ └── profile/ # 个人中心
│ └── ...
4. 开发规范
4.1 页面开发规范
4.1.1 页面结构
<template>
<view class="page-container">
<!-- 页面内容 -->
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
// 页面逻辑
const userStore = useUserStore()
onMounted(() => {
// 页面初始化逻辑
})
</script>
<style lang="scss" scoped>
.page-container {
padding: 20rpx;
}
</style>
4.1.2 组件开发规范
<template>
<view class="order-card">
<view class="order-header">
<text class="order-id">{{ order.id }}</text>
<StatusBadge :status="order.status" />
</view>
<view class="order-content">
<!-- 订单内容 -->
</view>
</view>
</template>
<script setup lang="ts">
interface Props {
order: OrderInfo
}
interface OrderInfo {
id: string
status: string
// 其他字段...
}
defineProps<Props>()
</script>
4.2 API 调用规范
4.2.1 请求封装
// utils/request.ts
import { useUserStore } from '@/stores/user'
interface RequestOptions {
url: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
data?: any
header?: Record<string, string>
}
export function request<T = any>(options: RequestOptions): Promise<T> {
const userStore = useUserStore()
return new Promise((resolve, reject) => {
uni.request({
url: `${import.meta.env.VITE_API_BASE_URL}${options.url}`,
method: options.method || 'GET',
data: options.data,
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userStore.token}`,
...options.header
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data as T)
} else {
reject(new Error(`请求失败: ${res.statusCode}`))
}
},
fail: (err) => {
reject(err)
}
})
})
}
4.2.2 API 接口定义
// api/order.ts
import { request } from '@/utils/request'
export interface OrderListParams {
page: number
pageSize: number
status?: string
}
export interface OrderInfo {
id: string
customerName: string
status: string
createTime: string
// 其他字段...
}
// 获取订单列表
export function getOrderList(params: OrderListParams) {
return request<{
list: OrderInfo[]
total: number
}>({
url: '/orders',
method: 'GET',
data: params
})
}
// 获取订单详情
export function getOrderDetail(id: string) {
return request<OrderInfo>({
url: `/orders/${id}`,
method: 'GET'
})
}
4.3 状态管理规范
// stores/order.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { OrderInfo } from '@/api/order'
export const useOrderStore = defineStore('order', () => {
const orderList = ref<OrderInfo[]>([])
const currentOrder = ref<OrderInfo | null>(null)
const loading = ref(false)
// 获取订单列表
async function fetchOrderList(params: any) {
loading.value = true
try {
const { getOrderList } = await import('@/api/order')
const result = await getOrderList(params)
orderList.value = result.list
return result
} finally {
loading.value = false
}
}
// 设置当前订单
function setCurrentOrder(order: OrderInfo) {
currentOrder.value = order
}
return {
orderList,
currentOrder,
loading,
fetchOrderList,
setCurrentOrder
}
})
5. 页面开发指南
5.1 员工小程序页面
5.1.1 订单管理页面
<!-- pages/order/list.vue -->
<template>
<view class="order-list-page">
<!-- 搜索栏 -->
<view class="search-bar">
<u-search
v-model="searchKeyword"
placeholder="搜索订单号或客户名称"
@search="handleSearch"
/>
</view>
<!-- 状态筛选 -->
<view class="filter-tabs">
<u-tabs
v-model="activeTab"
:list="statusTabs"
@change="handleTabChange"
/>
</view>
<!-- 订单列表 -->
<view class="order-list">
<OrderCard
v-for="order in orderList"
:key="order.id"
:order="order"
@click="handleOrderClick"
/>
</view>
<!-- 加载更多 -->
<u-loadmore
:status="loadStatus"
@loadmore="loadMore"
/>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useOrderStore } from '@/stores/order'
import OrderCard from '@/components/OrderCard.vue'
const orderStore = useOrderStore()
const searchKeyword = ref('')
const activeTab = ref(0)
const loadStatus = ref('loadmore')
const statusTabs = [
{ name: '全部' },
{ name: '待确认' },
{ name: '进行中' },
{ name: '已完成' }
]
// 搜索处理
function handleSearch() {
// 搜索逻辑
}
// 标签切换
function handleTabChange(index: number) {
activeTab.value = index
// 重新加载数据
}
// 订单点击
function handleOrderClick(order: any) {
uni.navigateTo({
url: `/pages/order/detail?id=${order.id}`
})
}
// 加载更多
function loadMore() {
// 加载更多逻辑
}
onMounted(() => {
// 初始化数据
orderStore.fetchOrderList({ page: 1, pageSize: 20 })
})
</script>
5.1.2 质检管理页面
<!-- pages/quality/check.vue -->
<template>
<view class="quality-check-page">
<u-form
:model="formData"
ref="formRef"
:rules="rules"
>
<!-- 基本信息 -->
<u-form-item label="订单号" prop="orderId">
<u-input v-model="formData.orderId" disabled />
</u-form-item>
<!-- 质检项目 -->
<u-form-item label="体重检测" prop="weight">
<u-input
v-model="formData.weight"
type="number"
placeholder="请输入体重(kg)"
/>
</u-form-item>
<!-- 健康状况 -->
<u-form-item label="健康状况" prop="healthStatus">
<u-radio-group v-model="formData.healthStatus">
<u-radio
v-for="item in healthOptions"
:key="item.value"
:label="item.label"
:name="item.value"
/>
</u-radio-group>
</u-form-item>
<!-- 照片上传 -->
<u-form-item label="质检照片">
<u-upload
:fileList="fileList"
@afterRead="afterRead"
@delete="deleteFile"
multiple
:maxCount="5"
/>
</u-form-item>
<!-- 备注 -->
<u-form-item label="备注">
<u-textarea
v-model="formData.remark"
placeholder="请输入质检备注"
/>
</u-form-item>
</u-form>
<!-- 提交按钮 -->
<view class="submit-section">
<u-button
type="primary"
@click="handleSubmit"
:loading="submitting"
>
提交质检报告
</u-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
const formRef = ref()
const submitting = ref(false)
const fileList = ref([])
const formData = reactive({
orderId: '',
weight: '',
healthStatus: '',
remark: ''
})
const rules = {
weight: [
{ required: true, message: '请输入体重', trigger: 'blur' }
],
healthStatus: [
{ required: true, message: '请选择健康状况', trigger: 'change' }
]
}
const healthOptions = [
{ label: '健康', value: 'healthy' },
{ label: '一般', value: 'normal' },
{ label: '异常', value: 'abnormal' }
]
// 文件上传
function afterRead(event: any) {
// 处理文件上传
}
// 删除文件
function deleteFile(event: any) {
// 处理文件删除
}
// 提交表单
async function handleSubmit() {
try {
await formRef.value.validate()
submitting.value = true
// 提交质检数据
// await submitQualityCheck(formData)
uni.showToast({
title: '提交成功',
icon: 'success'
})
// 返回上一页
uni.navigateBack()
} catch (error) {
console.error('提交失败:', error)
} finally {
submitting.value = false
}
}
</script>
5.2 司机小程序页面
5.2.1 运输任务页面
<!-- pages/task/list.vue -->
<template>
<view class="task-list-page">
<!-- 任务统计 -->
<view class="task-stats">
<view class="stat-item">
<text class="stat-number">{{ taskStats.pending }}</text>
<text class="stat-label">待接单</text>
</view>
<view class="stat-item">
<text class="stat-number">{{ taskStats.inProgress }}</text>
<text class="stat-label">进行中</text>
</view>
<view class="stat-item">
<text class="stat-number">{{ taskStats.completed }}</text>
<text class="stat-label">已完成</text>
</view>
</view>
<!-- 任务列表 -->
<view class="task-list">
<TaskCard
v-for="task in taskList"
:key="task.id"
:task="task"
@accept="handleAcceptTask"
@start="handleStartTask"
@complete="handleCompleteTask"
/>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import TaskCard from '@/components/TaskCard.vue'
const taskStats = ref({
pending: 0,
inProgress: 0,
completed: 0
})
const taskList = ref([])
// 接受任务
function handleAcceptTask(task: any) {
uni.showModal({
title: '确认接单',
content: '确定要接受这个运输任务吗?',
success: (res) => {
if (res.confirm) {
// 接受任务逻辑
}
}
})
}
// 开始运输
function handleStartTask(task: any) {
uni.navigateTo({
url: `/pages/task/transport?id=${task.id}`
})
}
// 完成任务
function handleCompleteTask(task: any) {
uni.navigateTo({
url: `/pages/task/complete?id=${task.id}`
})
}
onMounted(() => {
// 加载任务数据
})
</script>
6. 组件开发
6.1 通用组件
6.1.1 状态标签组件
<!-- components/StatusBadge.vue -->
<template>
<view :class="['status-badge', `status-${status}`]">
<text class="status-text">{{ statusText }}</text>
</view>
</template>
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
status: string
}
const props = defineProps<Props>()
const statusText = computed(() => {
const statusMap: Record<string, string> = {
'pending': '待处理',
'processing': '处理中',
'completed': '已完成',
'cancelled': '已取消'
}
return statusMap[props.status] || '未知状态'
})
</script>
<style lang="scss" scoped>
.status-badge {
display: inline-flex;
align-items: center;
padding: 4rpx 12rpx;
border-radius: 12rpx;
font-size: 24rpx;
&.status-pending {
background-color: #fff3cd;
color: #856404;
}
&.status-processing {
background-color: #d1ecf1;
color: #0c5460;
}
&.status-completed {
background-color: #d4edda;
color: #155724;
}
&.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
}
</style>
6.1.2 订单卡片组件
<!-- components/OrderCard.vue -->
<template>
<view class="order-card" @click="handleClick">
<view class="card-header">
<text class="order-id">订单号: {{ order.id }}</text>
<StatusBadge :status="order.status" />
</view>
<view class="card-content">
<view class="info-row">
<text class="label">客户名称:</text>
<text class="value">{{ order.customerName }}</text>
</view>
<view class="info-row">
<text class="label">数量:</text>
<text class="value">{{ order.quantity }}头</text>
</view>
<view class="info-row">
<text class="label">创建时间:</text>
<text class="value">{{ formatTime(order.createTime) }}</text>
</view>
</view>
<view class="card-actions" v-if="showActions">
<u-button
size="small"
type="primary"
@click.stop="handleAction('detail')"
>
查看详情
</u-button>
<u-button
size="small"
@click.stop="handleAction('edit')"
v-if="order.status === 'pending'"
>
编辑
</u-button>
</view>
</view>
</template>
<script setup lang="ts">
import { formatTime } from '@/utils/format'
import StatusBadge from './StatusBadge.vue'
interface Props {
order: {
id: string
customerName: string
quantity: number
status: string
createTime: string
}
showActions?: boolean
}
interface Emits {
(e: 'click', order: any): void
(e: 'action', action: string, order: any): void
}
const props = withDefaults(defineProps<Props>(), {
showActions: true
})
const emit = defineEmits<Emits>()
function handleClick() {
emit('click', props.order)
}
function handleAction(action: string) {
emit('action', action, props.order)
}
</script>
<style lang="scss" scoped>
.order-card {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.order-id {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
}
.card-content {
.info-row {
display: flex;
margin-bottom: 12rpx;
.label {
width: 160rpx;
font-size: 26rpx;
color: #666;
}
.value {
flex: 1;
font-size: 26rpx;
color: #333;
}
}
}
.card-actions {
display: flex;
gap: 20rpx;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #eee;
}
}
</style>
7. 工具函数
7.1 认证工具
// utils/auth.ts
import { useUserStore } from '@/stores/user'
// 检查登录状态
export function checkAuth(): boolean {
const userStore = useUserStore()
return !!userStore.token
}
// 跳转到登录页
export function redirectToLogin() {
uni.navigateTo({
url: '/pages/auth/login'
})
}
// 登出
export function logout() {
const userStore = useUserStore()
userStore.clearUserInfo()
uni.reLaunch({
url: '/pages/auth/login'
})
}
// 微信登录
export function wxLogin(): Promise<string> {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => {
resolve(res.code)
},
fail: (err) => {
reject(err)
}
})
})
}
7.2 格式化工具
// utils/format.ts
import dayjs from 'dayjs'
// 格式化时间
export function formatTime(time: string | Date, format = 'YYYY-MM-DD HH:mm:ss'): string {
return dayjs(time).format(format)
}
// 格式化金额
export function formatMoney(amount: number): string {
return `¥${amount.toFixed(2)}`
}
// 格式化手机号
export function formatPhone(phone: string): string {
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}
// 格式化文件大小
export function formatFileSize(size: number): string {
if (size < 1024) {
return `${size}B`
} else if (size < 1024 * 1024) {
return `${(size / 1024).toFixed(1)}KB`
} else {
return `${(size / (1024 * 1024)).toFixed(1)}MB`
}
}
8. 调试与测试
8.1 真机调试
# 生成预览二维码
npm run build:mp-weixin
# 在微信开发者工具中预览
8.2 性能监控
// utils/performance.ts
export function trackPagePerformance(pageName: string) {
const startTime = Date.now()
return {
end: () => {
const endTime = Date.now()
const duration = endTime - startTime
console.log(`页面 ${pageName} 加载耗时: ${duration}ms`)
// 上报性能数据
// reportPerformance(pageName, duration)
}
}
}
8.3 错误监控
// utils/error.ts
export function setupErrorHandler() {
// 全局错误处理
uni.onError((error) => {
console.error('小程序错误:', error)
// 上报错误信息
// reportError(error)
})
// 未处理的 Promise 错误
uni.onUnhandledRejection((event) => {
console.error('未处理的 Promise 错误:', event.reason)
// 上报错误信息
// reportError(event.reason)
})
}
9. 发布部署
9.1 构建配置
// package.json
{
"scripts": {
"dev:mp-weixin": "uni -p mp-weixin",
"build:mp-weixin": "uni build -p mp-weixin",
"dev:h5": "uni -p h5",
"build:h5": "uni build -p h5"
}
}
9.2 发布流程
- 代码检查
npm run lint
npm run type-check
- 构建生产版本
npm run build:mp-weixin
- 上传代码
- 使用微信开发者工具上传代码
- 填写版本号和更新说明
- 提交审核
- 在微信公众平台提交审核
- 等待审核通过后发布
10. 常见问题
10.1 开发问题
Q: 小程序白屏问题
- 检查页面路径配置
- 查看控制台错误信息
- 确认组件引入是否正确
Q: 网络请求失败
- 检查域名配置
- 确认 HTTPS 证书
- 查看请求参数格式
10.2 性能问题
Q: 页面加载慢
- 优化图片大小
- 减少组件层级
- 使用分包加载
Q: 内存占用高
- 及时清理定时器
- 避免内存泄漏
- 优化数据结构
11. 联系信息
- 小程序开发: miniprogram-dev@company.com
- 技术支持: tech-support@company.com
- 文档维护: docs@company.com