# mini-demo 迁移指导规范 ## 版本信息 | 版本 | 日期 | 描述 | 作者 | |------|------|------|------| | 2.1 | 2025-10-15 | 修正Taro测试位置,统一使用mini/tests/目录 | Winston | | 2.0 | 2025-10-15 | 整合精确样式迁移内容,优化文档结构 | Winston | | 1.0 | 2025-10-15 | 初始mini-demo迁移指导规范 | Winston | ## 概述 本文档提供从原生微信小程序(mini-demo)迁移到Taro + React技术栈的完整指导,包含**百分百样式保留**的精确迁移方案,确保迁移过程顺利且保持用户体验一致性。 ## 迁移策略 ### 整体迁移方法 - **渐进式迁移**: 逐个页面迁移,保持业务连续性 - **技术栈转换**: 原生小程序 → Taro + React + TypeScript - **样式重构**: WXSS → Tailwind CSS + shadcn/ui(精确样式保留) - **数据集成**: 模拟数据 → 真实后端API ### 迁移优先级 1. **高优先级**: 核心出行流程页面 2. **中优先级**: 用户管理相关页面 3. **低优先级**: 辅助功能和设置页面 ## 页面迁移对照表 ### 核心页面迁移 | 原页面 | 目标页面 | 状态 | 复杂度 | |--------|----------|------|--------| | `mini-demo/pages/home/home` | `mini/src/pages/home/index.tsx` | ✅ 待迁移 | 中等 | | `mini-demo/pages/select-activity/select-activity` | `mini/src/pages/activity-select/index.tsx` | ✅ 待迁移 | 中等 | | `mini-demo/pages/schedule-list/schedule-list` | `mini/src/pages/schedule-list/index.tsx` | ✅ 待迁移 | 中等 | | `mini-demo/pages/order-detail/order-detail` | `mini/src/pages/order-detail/index.tsx` | 🔄 规划中 | 高 | | `mini-demo/pages/passenger/passenger` | `mini/src/pages/passenger/index.tsx` | 🔄 规划中 | 中等 | ## 组件映射规范 ### 基础组件映射 | 原生小程序组件 | Taro组件 | 说明 | |----------------|----------|------| | `` | `` | 视图容器 | | `` | `` | 文本组件 | | ` ``` ### 常用样式转换表 | WXSS属性 | Tailwind类名 | 示例 | |----------|--------------|------| | `width: 100%` | `w-full` | `w-full` | | `height: 100%` | `h-full` | `h-full` | | `margin: 20rpx` | `m-5` | `m-5` | | `padding: 20rpx` | `p-5` | `p-5` | | `background-color: #fff` | `bg-white` | `bg-white` | | `color: #333` | `text-gray-900` | `text-gray-900` | | `font-size: 32rpx` | `text-[32rpx]` | `text-[32rpx]` | | `border-radius: 8rpx` | `rounded-[8rpx]` | `rounded-[8rpx]` | ## 数据层迁移 ### 数据模型转换 **原数据格式:** ```javascript // mini-demo 模拟数据 const routeData = { id: 1, startPoint: "北京", endPoint: "上海", departureTime: "2025-10-15 08:00:00", price: 200, vehicleType: "商务车" } ``` **新数据格式:** ```typescript // TypeScript接口定义 interface Route { id: number; startPoint: string; endPoint: string; departureTime: Date; price: number; vehicleType: VehicleType; } // API调用 const { data: routes } = useQuery({ queryKey: ['routes', queryParams], queryFn: () => api.routes.list(queryParams) }) ``` ### API集成 **数据获取模式:** ```typescript // 使用React Query管理服务端状态 import { useQuery } from '@tanstack/react-query' import { api } from '@/utils/api' export function useRoutes(queryParams: RouteQueryParams) { return useQuery({ queryKey: ['routes', queryParams], queryFn: () => api.routes.list(queryParams), staleTime: 5 * 60 * 1000, // 5分钟缓存 }) } ``` ## 页面迁移详细指导 ### 首页迁移 (home/home) **原页面结构分析:** ```xml 大巴拼车 {{startLocation || '选择出发地'}} {{endLocation || '选择目的地'}} {{selectedDate || '选择日期'}} ``` **迁移后的React组件:** ```tsx // mini/src/pages/home/index.tsx import { View, Text, Button } from '@tarojs/components' import { useLoad } from '@tarojs/taro' import { BannerCarousel } from '@/components/home/BannerCarousel' import { TravelTypeSelector } from '@/components/home/TravelTypeSelector' import { LocationPicker } from '@/components/home/LocationPicker' import { DatePicker } from '@/components/home/DatePicker' export default function HomePage() { const [travelType, setTravelType] = useState('carpool') const [startLocation, setStartLocation] = useState('') const [endLocation, setEndLocation] = useState('') const [selectedDate, setSelectedDate] = useState(new Date()) useLoad(() => { console.log('HomePage loaded') }) const handleSearch = () => { // 导航到活动选择页面 navigateTo({ url: `/pages/activity-select?type=${travelType}&start=${startLocation}&end=${endLocation}&date=${selectedDate.toISOString()}` }) } return ( {/* 轮播海报 - MVP限制:使用固定静态图片 */} {/* 出行方式选择 */} {/* 地点选择器 */} {/* 日期选择器 */} {/* 查询按钮 */} ) } ``` ### 活动选择页面迁移 (select-activity/select-activity) **迁移要点:** ```tsx // mini/src/pages/activity-select/index.tsx export default function ActivitySelectPage() { const router = useRouter() const { type, start, end, date } = router.params // 根据查询参数获取活动列表 const { data: activities, isLoading } = useActivities({ type: type as ActivityType, startPoint: start, endPoint: end, date: date ? new Date(date) : new Date() }) return ( {/* 活动类型筛选 */} { // 更新URL参数并重新查询 }} /> {/* 活动列表 */} {isLoading ? ( ) : ( {activities?.map(activity => ( { // 导航到班次列表页面 navigateTo({ url: `/pages/schedule-list?activityId=${activity.id}` }) }} /> ))} )} ) } ``` ### 班次列表页面迁移 (schedule-list/schedule-list) **迁移要点:** ```tsx // mini/src/pages/schedule-list/index.tsx export default function ScheduleListPage() { const router = useRouter() const { activityId } = router.params const [sortBy, setSortBy] = useState<'price' | 'departureTime'>('departureTime') const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc') // 获取路线列表 const { data: routes, isLoading } = useRoutes({ activityId: Number(activityId), sortBy, sortOrder }) return ( {/* 排序和筛选工具栏 */} {/* 路线列表 */} {routes?.map(route => ( { // 导航到订单确认页面 navigateTo({ url: `/pages/order-confirm?routeId=${route.id}` }) }} /> ))} ) } ``` ## 事件处理迁移 ### 事件绑定转换 | 原事件 | React事件 | 示例 | |--------|-----------|------| | `bindtap` | `onClick` | ` ``` ### 订单页面样式迁移 #### 包车主题 ```tsx // 原WXSS .charter-theme { background: linear-gradient(180deg, #1a1a1a 0%, #2d2d2d 100%); } // Tailwind迁移 {/* 包车主题内容 */} ``` #### 支付区域 ```tsx // 原WXSS .pay-section { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; padding: 24rpx 32rpx; box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.1); } // Tailwind迁移 {/* 支付内容 */} ``` ### 我的页面样式迁移 #### 用户信息区域 ```tsx // 原WXSS .user-section { background: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%); padding: 40rpx 32rpx 32rpx 32rpx; color: #fff; } // Tailwind迁移 {/* 用户信息内容 */} ``` #### 会员卡片 ```tsx // 原WXSS .member-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20rpx; padding: 24rpx; color: #fff; box-shadow: 0 6rpx 24rpx rgba(102, 126, 234, 0.3); } // Tailwind迁移 {/* 会员卡片内容 */} ``` ## 响应式设计迁移 ### 媒体查询转换 ```tsx // 原WXSS @media screen and (max-height: 667px) { .banner-container { height: 22vh; } .booking-section { padding: 16rpx; } } // Tailwind迁移 {/* 响应式内容 */} ``` ## 动画效果迁移 ### 渐入动画 ```tsx // 原WXSS .fade-in { animation: fadeIn 0.3s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20rpx); } to { opacity: 1; transform: translateY(0); } } // Tailwind迁移 {/* 动画内容 */} // 在全局CSS中添加 @keyframes fadeIn { from { opacity: 0; transform: translateY(20rpx); } to { opacity: 1; transform: translateY(0); } } ``` ### 视觉一致性检查清单 - [ ] 颜色系统完全匹配 - [ ] 渐变效果一致 - [ ] 阴影层次相同 - [ ] 圆角设计一致 - [ ] 间距布局相同 - [ ] 字体大小和权重一致 - [ ] 按钮交互效果相同 - [ ] 包车主题效果一致 --- **文档状态**: 正式版 **下次评审**: 2025-11-15