1046 lines
21 KiB
Markdown
1046 lines
21 KiB
Markdown
|
|
# 小程序开发指南
|
||
|
|
|
||
|
|
## 版本历史
|
||
|
|
|
||
|
|
| 版本 | 日期 | 修改人 | 修改内容 |
|
||
|
|
|------|------|--------|----------|
|
||
|
|
| 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 工具安装
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 安装 HBuilderX (推荐)
|
||
|
|
# 下载地址: https://www.dcloud.io/hbuilderx.html
|
||
|
|
|
||
|
|
# 或使用 VS Code + uni-app 插件
|
||
|
|
# 安装 uni-app 插件
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.2 微信开发者工具
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 下载微信开发者工具
|
||
|
|
# 下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
|
||
|
|
|
||
|
|
# 配置小程序 AppID
|
||
|
|
# 在各小程序的 manifest.json 中配置对应的 AppID
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.3 项目初始化
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 进入小程序目录
|
||
|
|
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 页面结构
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<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 组件开发规范
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<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 请求封装
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 接口定义
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 状态管理规范
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 订单管理页面
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<!-- 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 质检管理页面
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<!-- 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 运输任务页面
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<!-- 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 状态标签组件
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<!-- 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 订单卡片组件
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<!-- 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 认证工具
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 格式化工具
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 真机调试
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 生成预览二维码
|
||
|
|
npm run build:mp-weixin
|
||
|
|
# 在微信开发者工具中预览
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 性能监控
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 错误监控
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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 构建配置
|
||
|
|
|
||
|
|
```json
|
||
|
|
// 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 发布流程
|
||
|
|
|
||
|
|
1. **代码检查**
|
||
|
|
```bash
|
||
|
|
npm run lint
|
||
|
|
npm run type-check
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **构建生产版本**
|
||
|
|
```bash
|
||
|
|
npm run build:mp-weixin
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **上传代码**
|
||
|
|
- 使用微信开发者工具上传代码
|
||
|
|
- 填写版本号和更新说明
|
||
|
|
|
||
|
|
4. **提交审核**
|
||
|
|
- 在微信公众平台提交审核
|
||
|
|
- 等待审核通过后发布
|
||
|
|
|
||
|
|
## 10. 常见问题
|
||
|
|
|
||
|
|
### 10.1 开发问题
|
||
|
|
|
||
|
|
**Q: 小程序白屏问题**
|
||
|
|
- 检查页面路径配置
|
||
|
|
- 查看控制台错误信息
|
||
|
|
- 确认组件引入是否正确
|
||
|
|
|
||
|
|
**Q: 网络请求失败**
|
||
|
|
- 检查域名配置
|
||
|
|
- 确认 HTTPS 证书
|
||
|
|
- 查看请求参数格式
|
||
|
|
|
||
|
|
### 10.2 性能问题
|
||
|
|
|
||
|
|
**Q: 页面加载慢**
|
||
|
|
- 优化图片大小
|
||
|
|
- 减少组件层级
|
||
|
|
- 使用分包加载
|
||
|
|
|
||
|
|
**Q: 内存占用高**
|
||
|
|
- 及时清理定时器
|
||
|
|
- 避免内存泄漏
|
||
|
|
- 优化数据结构
|
||
|
|
|
||
|
|
## 11. 联系信息
|
||
|
|
|
||
|
|
- **小程序开发**: miniprogram-dev@company.com
|
||
|
|
- **技术支持**: tech-support@company.com
|
||
|
|
- **文档维护**: docs@company.com
|
||
|
|
|
||
|
|
## 相关文档
|
||
|
|
|
||
|
|
- [开发指南](./开发指南.md)
|
||
|
|
- [产品需求文档](./产品需求文档.md)
|
||
|
|
- [API接口文档](./API接口文档.md)
|
||
|
|
- [测试指南](./测试指南.md)
|