# 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组件 | 说明 | |----------------|----------|------| | `` | `` | 视图容器 | | `` | `` | 文本组件 | | ` // 可用变体 ``` #### Dialog 组件 ```typescript // 正确引入方式 import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' // 使用示例 对话框标题 对话框内容 ``` #### 禁止使用的导入方式 ```typescript // ❌ 错误 - 使用Taro原生Button import { Button } from '@tarojs/components' // ❌ 错误 - 使用自定义模态框 import Modal from '@/components/modal' // ✅ 正确 - 使用shadcn/ui组件 import { Button } from '@/components/ui/button' import { Dialog } from '@/components/ui/dialog' ``` ### 自定义组件映射 | 原组件 | 新组件 | 位置 | |--------|--------|------| | 轮播图组件 | `BannerCarousel` | `mini/src/components/home/BannerCarousel.tsx` | | 出行方式选择 | `TravelTypeSelector` | `mini/src/components/home/TravelTypeSelector.tsx` | | 地点选择器 | `LocationPicker` | `mini/src/components/home/LocationPicker.tsx` | | 活动筛选器 | `ActivityFilter` | `mini/src/components/home/ActivityFilter.tsx` | | 路线卡片 | `RouteCard` | `mini/src/components/home/RouteCard.tsx` | ## 样式转换规范 ### WXSS → Tailwind CSS 转换 **原WXSS样式:** ```css /* mini-demo/pages/home/home.wxss */ .container { padding: 20rpx; background-color: #ffffff; } .title { font-size: 32rpx; font-weight: bold; color: #333333; } .button { background-color: #007AFF; color: #ffffff; border-radius: 8rpx; padding: 20rpx 40rpx; } ``` **转换后的Tailwind样式:** ```tsx // mini/src/pages/home/index.tsx 标题 ``` ### 常用样式转换表 | 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: "商务车", travelMode: "包车" } ``` **新数据格式:** ```typescript // TypeScript接口定义 interface Route { id: number; startPoint: string; endPoint: string; departureTime: Date; price: number; vehicleType: VehicleType; travelMode: TravelMode; // 新增:出行方式(拼车/包车) } // 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); } } ``` ### 视觉一致性检查清单 - [ ] 颜色系统完全匹配 - [ ] 渐变效果一致 - [ ] 阴影层次相同 - [ ] 圆角设计一致 - [ ] 间距布局相同 - [ ] 字体大小和权重一致 - [ ] 按钮交互效果相同 - [ ] 包车主题效果一致 ## Navbar 导航栏样式规范 ### 基础样式定义 #### 高度和布局 ```css /* 导航栏总高度计算 */ --navbar-height: 44px --status-bar-height: 系统状态栏高度 --total-height: status-bar-height + navbar-height ``` #### 颜色系统 ```css /* 背景色 */ --navbar-bg-default: #ffffff --navbar-bg-dark: #1a1a1a --navbar-bg-primary: #4A90C2 --navbar-bg-transparent: transparent /* 文字色 */ --navbar-text-default: #333333 --navbar-text-light: #ffffff --navbar-text-primary: #ffffff ``` ### 预设样式变体 #### 默认导航栏 ```tsx // 样式配置 { backgroundColor: 'bg-white', textColor: 'text-gray-900', border: true, } // Tailwind 类名 className="bg-white text-gray-900 border-b border-gray-200" ``` #### 深色导航栏 ```tsx // 样式配置 { backgroundColor: 'bg-gray-900', textColor: 'text-white', border: true, } // Tailwind 类名 className="bg-gray-900 text-white border-b border-gray-700" ``` #### 透明导航栏 ```tsx // 样式配置 { backgroundColor: 'bg-transparent', textColor: 'text-white', border: false, } // Tailwind 类名 className="bg-transparent text-white" ``` #### 主色调导航栏 ```tsx // 样式配置 { backgroundColor: 'bg-primary', textColor: 'text-white', border: false, } // Tailwind 类名 className="bg-primary text-white" ``` ### 平台适配规范 #### 微信小程序 ```tsx // 高度计算 const NAVBAR_HEIGHT = 44 const STATUS_BAR_HEIGHT = systemInfo.statusBarHeight || 0 const TOTAL_HEIGHT = STATUS_BAR_HEIGHT + NAVBAR_HEIGHT // 胶囊按钮避让 const menuButtonInfo = Taro.getMenuButtonBoundingClientRect() const rightPosition = systemInfo.screenWidth - menuButtonInfo.left + 10 // 标题宽度限制 const titleMaxWidth = `calc(100% - ${rightPosition}px - 60px - 60px)` ``` #### H5 环境 ```tsx // 标准布局 left: '12px' // left-3 right: '12px' // right-3 ``` ### 使用示例 #### 基础导航栏 ```tsx Taro.navigateBack()} onClickRight={handleComplete} /> ``` #### 自定义样式导航栏 ```tsx ``` ## Dialog 对话框样式规范 ### 基础样式定义 #### 遮罩层 ```tsx // 遮罩样式 className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" ``` #### 对话框容器 ```tsx // 对话框基础样式 className="relative bg-white rounded-lg shadow-lg max-w-md w-full mx-4" // 阴影系统 --dialog-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) ``` ### 组件结构样式 #### DialogContent 内容区域 ```tsx // 基础内边距 className="p-6" // 自定义样式 className={cn("p-6", className)} ``` #### DialogHeader 头部区域 ```tsx // 底部外边距 className="mb-4" // 自定义样式 className={cn("mb-4", className)} ``` #### DialogTitle 标题 ```tsx // 标题样式 className="text-lg font-semibold text-gray-900" // 自定义样式 className={cn("text-lg font-semibold text-gray-900", className)} ``` #### DialogFooter 底部区域 ```tsx // 按钮布局 className="flex justify-end space-x-2" // 自定义样式 className={cn("flex justify-end space-x-2", className)} ``` ### 预设变体样式 #### 确认对话框 ```tsx 确认操作 确定要执行此操作吗? ``` #### 表单对话框 ```tsx 添加乘客 ``` #### 警告对话框 ```tsx 警告 此操作不可撤销,请谨慎操作。 ``` ### 迁移指南 #### Navbar 迁移 ```tsx // 原小程序 // 迁移后 ``` #### Dialog 迁移 ```tsx // 原小程序 内容 // 迁移后 内容 ``` --- **文档状态**: 正式版 **下次评审**: 2025-11-15