Selaa lähdekoodia

📝 docs(architecture): 新增Taro小程序和Tailwind规范文档

- 新增【Taro小程序开发规范】文档,定义Taro开发最佳实践和项目结构规范
- 新增【Tailwind CSS样式规范】文档,定义原子化样式使用规范和响应式设计
- 新增【mini-demo迁移指导规范】文档,提供从原生小程序到Taro的完整迁移方案
- 更新架构文档索引,添加新规范文档的引用链接
- 更新开发故事文档,引用新增的规范文档作为开发指导
yourname 4 kuukautta sitten
vanhempi
sitoutus
feabc8e0b5

+ 6 - 0
docs/architecture.md

@@ -17,6 +17,12 @@
 
 **注意**: 本项目的详细架构文档已拆分为多个子文档,位于 `docs/architecture/` 目录中。如需查看完整的架构文档结构,请参阅 [架构文档索引](./architecture/index.md)。
 
+### 新增Taro小程序和Tailwind规范
+基于mini项目迁移需求,新增以下规范文档:
+- [Taro小程序开发规范](./architecture/taro-mini-program-standards.md) - Taro小程序开发最佳实践
+- [Tailwind CSS样式规范](./architecture/tailwind-css-standards.md) - Tailwind CSS使用规范
+- [mini-demo迁移指导规范](./architecture/mini-demo-migration-guide.md) - 从原生小程序迁移到Taro的详细指导(包含精确样式迁移)
+
 ### 文档范围
 全面定义出行服务项目的架构方案、技术栈选择、数据模型设计和集成策略
 

+ 18 - 0
docs/architecture/coding-standards.md

@@ -29,3 +29,21 @@
 - **Schema设计**: 创建、更新、响应使用不同的Zod schema
 - **路由生成**: 使用 `createCrudRoutes` 自动生成API路由
 - **用户跟踪**: 实现用户跟踪功能记录操作人信息
+
+## Taro小程序开发规范
+- **组件开发**: 遵循 [Taro小程序开发规范](./taro-mini-program-standards.md) 进行开发
+- **平台适配**: 正确处理微信小程序、H5等平台差异
+- **状态管理**: 使用React Query管理服务端状态
+- **路由导航**: 统一使用Taro路由API
+
+## Tailwind CSS样式规范
+- **样式开发**: 遵循 [Tailwind CSS样式规范](./tailwind-css-standards.md) 进行开发
+- **原子化原则**: 优先使用Tailwind原子类,避免自定义CSS
+- **响应式设计**: 移动端优先的响应式布局
+- **组件样式**: 使用shadcn/ui组件库和变体系统
+
+## mini-demo迁移规范
+- **迁移指导**: 遵循 [mini-demo迁移指导规范](./mini-demo-migration-guide.md) 进行迁移
+- **样式保留**: 确保百分百样式保留,保持用户体验一致性
+- **技术栈转换**: 原生小程序 → Taro + React + TypeScript
+- **数据集成**: 模拟数据 → 真实后端API

+ 927 - 0
docs/architecture/mini-demo-migration-guide.md

@@ -0,0 +1,927 @@
+# mini-demo 迁移指导规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 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组件 | 说明 |
+|----------------|----------|------|
+| `<view>` | `<View>` | 视图容器 |
+| `<text>` | `<Text>` | 文本组件 |
+| `<button>` | `<Button>` | 按钮组件 |
+| `<input>` | `<Input>` | 输入框组件 |
+| `<image>` | `<Image>` | 图片组件 |
+| `<scroll-view>` | `<ScrollView>` | 滚动视图 |
+| `<swiper>` | `<Swiper>` | 轮播组件 |
+
+### 自定义组件映射
+
+| 原组件 | 新组件 | 位置 |
+|--------|--------|------|
+| 轮播图组件 | `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
+<View className="p-5 bg-white">
+  <Text className="text-[32rpx] font-bold text-gray-900">标题</Text>
+  <Button className="bg-blue-500 text-white rounded-[8rpx] px-10 py-5">
+    按钮
+  </Button>
+</View>
+```
+
+### 常用样式转换表
+
+| 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
+<!-- mini-demo/pages/home/home.wxml -->
+<view class="container">
+  <!-- 轮播海报 -->
+  <swiper class="banner-swiper" indicator-dots="true" autoplay="true">
+    <swiper-item wx:for="{{bannerList}}" wx:key="id">
+      <image src="{{item.imageUrl}}" mode="aspectFill"></image>
+    </swiper-item>
+  </swiper>
+
+  <!-- 出行方式选择 -->
+  <view class="travel-type-selector">
+    <view class="type-item {{activeType === 'carpool' ? 'active' : ''}}"
+          bindtap="onTypeSelect" data-type="carpool">
+      大巴拼车
+    </view>
+    <!-- 其他出行方式 -->
+  </view>
+
+  <!-- 地点选择 -->
+  <view class="location-picker">
+    <view class="start-location" bindtap="onStartLocationSelect">
+      {{startLocation || '选择出发地'}}
+    </view>
+    <view class="end-location" bindtap="onEndLocationSelect">
+      {{endLocation || '选择目的地'}}
+    </view>
+  </view>
+
+  <!-- 日期选择 -->
+  <view class="date-picker" bindtap="onDateSelect">
+    {{selectedDate || '选择日期'}}
+  </view>
+
+  <!-- 查询按钮 -->
+  <button class="search-btn" bindtap="onSearch">查询路线</button>
+</view>
+```
+
+**迁移后的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<TravelType>('carpool')
+  const [startLocation, setStartLocation] = useState('')
+  const [endLocation, setEndLocation] = useState('')
+  const [selectedDate, setSelectedDate] = useState<Date>(new Date())
+
+  useLoad(() => {
+    console.log('HomePage loaded')
+  })
+
+  const handleSearch = () => {
+    // 导航到活动选择页面
+    navigateTo({
+      url: `/pages/activity-select?type=${travelType}&start=${startLocation}&end=${endLocation}&date=${selectedDate.toISOString()}`
+    })
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      {/* 轮播海报 - MVP限制:使用固定静态图片 */}
+      <BannerCarousel banners={[{ id: 1, imageUrl: '/static/home-banner.jpg' }]} />
+
+      {/* 出行方式选择 */}
+      <TravelTypeSelector
+        value={travelType}
+        onChange={setTravelType}
+      />
+
+      {/* 地点选择器 */}
+      <LocationPicker
+        startLocation={startLocation}
+        endLocation={endLocation}
+        onStartChange={setStartLocation}
+        onEndChange={setEndLocation}
+      />
+
+      {/* 日期选择器 */}
+      <DatePicker
+        value={selectedDate}
+        onChange={setSelectedDate}
+      />
+
+      {/* 查询按钮 */}
+      <Button
+        className="w-full bg-blue-500 text-white py-3 rounded-lg mt-6"
+        onClick={handleSearch}
+        disabled={!startLocation || !endLocation}
+      >
+        查询路线
+      </Button>
+    </View>
+  )
+}
+```
+
+### 活动选择页面迁移 (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 (
+    <View className="min-h-screen bg-white">
+      {/* 活动类型筛选 */}
+      <ActivityFilter
+        value={type as ActivityType}
+        onChange={(newType) => {
+          // 更新URL参数并重新查询
+        }}
+      />
+
+      {/* 活动列表 */}
+      {isLoading ? (
+        <LoadingSpinner />
+      ) : (
+        <View className="space-y-4 p-4">
+          {activities?.map(activity => (
+            <ActivityCard
+              key={activity.id}
+              activity={activity}
+              onSelect={() => {
+                // 导航到班次列表页面
+                navigateTo({
+                  url: `/pages/schedule-list?activityId=${activity.id}`
+                })
+              }}
+            />
+          ))}
+        </View>
+      )}
+    </View>
+  )
+}
+```
+
+### 班次列表页面迁移 (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 (
+    <View className="min-h-screen bg-gray-50">
+      {/* 排序和筛选工具栏 */}
+      <Toolbar
+        sortBy={sortBy}
+        sortOrder={sortOrder}
+        onSortChange={setSortBy}
+        onOrderChange={setSortOrder}
+      />
+
+      {/* 路线列表 */}
+      <View className="space-y-3 p-4">
+        {routes?.map(route => (
+          <RouteCard
+            key={route.id}
+            route={route}
+            onSelect={() => {
+              // 导航到订单确认页面
+              navigateTo({
+                url: `/pages/order-confirm?routeId=${route.id}`
+              })
+            }}
+          />
+        ))}
+      </View>
+    </View>
+  )
+}
+```
+
+## 事件处理迁移
+
+### 事件绑定转换
+
+| 原事件 | React事件 | 示例 |
+|--------|-----------|------|
+| `bindtap` | `onClick` | `<Button onClick={handleClick}>` |
+| `bindinput` | `onInput` | `<Input onInput={handleInput}>` |
+| `bindchange` | `onChange` | `<Picker onChange={handleChange}>` |
+| `catchtap` | `onClick` + 阻止冒泡 | 在事件处理函数中处理 |
+
+### 数据传递转换
+
+**原小程序方式:**
+```xml
+<view bindtap="onItemTap" data-id="{{item.id}}" data-name="{{item.name}}">
+  点击我
+</view>
+```
+
+```javascript
+// 页面JS
+onItemTap: function(e) {
+  const id = e.currentTarget.dataset.id
+  const name = e.currentTarget.dataset.name
+  // 处理逻辑
+}
+```
+
+**React方式:**
+```tsx
+<View onClick={() => onItemTap(item.id, item.name)}>
+  点击我
+</View>
+```
+
+```typescript
+// 组件函数
+const onItemTap = (id: number, name: string) => {
+  // 处理逻辑
+}
+```
+
+## 状态管理迁移
+
+### 页面状态转换
+
+**原小程序状态:**
+```javascript
+// mini-demo页面JS
+Page({
+  data: {
+    bannerList: [],
+    activeType: 'carpool',
+    startLocation: '',
+    endLocation: '',
+    selectedDate: ''
+  },
+
+  onLoad: function() {
+    this.setData({
+      bannerList: mockBannerData
+    })
+  },
+
+  onTypeSelect: function(e) {
+    this.setData({
+      activeType: e.currentTarget.dataset.type
+    })
+  }
+})
+```
+
+**React状态管理:**
+```tsx
+// React组件
+import { useState, useEffect } from 'react'
+
+export default function HomePage() {
+  const [bannerList, setBannerList] = useState<Banner[]>([])
+  const [activeType, setActiveType] = useState<TravelType>('carpool')
+  const [startLocation, setStartLocation] = useState('')
+  const [endLocation, setEndLocation] = useState('')
+  const [selectedDate, setSelectedDate] = useState<Date>(new Date())
+
+  useEffect(() => {
+    // 初始化数据
+    setBannerList([{ id: 1, imageUrl: '/static/home-banner.jpg' }])
+  }, [])
+
+  const handleTypeSelect = (type: TravelType) => {
+    setActiveType(type)
+  }
+
+  return (
+    // JSX内容
+  )
+}
+```
+
+## 测试迁移指导
+
+### 单元测试迁移
+
+**原测试结构:**
+```javascript
+// mini-demo测试(通常较少)
+```
+
+**新测试结构:**
+```typescript
+// tests/unit/mini/pages/home/index.test.tsx
+import { render } from '@testing-library/react'
+import HomePage from '@/pages/home/index'
+
+describe('HomePage', () => {
+  it('renders travel type selector', () => {
+    const { getByText } = render(<HomePage />)
+    expect(getByText('大巴拼车')).toBeInTheDocument()
+  })
+
+  it('handles location selection', () => {
+    // 测试地点选择功能
+  })
+})
+```
+
+### E2E测试迁移
+
+**Playwright测试:**
+```typescript
+// tests/e2e/travel-flow/home-page.spec.ts
+test('首页查询流程', async ({ page }) => {
+  await page.goto('/pages/home/index')
+
+  // 选择出行方式
+  await page.click('[data-testid="travel-type-carpool"]')
+
+  // 选择出发地和目的地
+  await page.fill('[data-testid="start-location"]', '北京')
+  await page.fill('[data-testid="end-location"]', '上海')
+
+  // 点击查询
+  await page.click('[data-testid="search-button"]')
+
+  // 验证跳转到活动选择页面
+  await expect(page).toHaveURL(/.*pages\/activity-select.*/)
+})
+```
+
+## 常见问题解决
+
+### 样式兼容性问题
+
+**问题**: 某些Tailwind类名在小程序中不生效
+**解决方案**: 使用 `weapp-tailwindcss` 插件进行适配
+
+```javascript
+// tailwind.config.js
+module.exports = {
+  corePlugins: {
+    preflight: false, // 禁用preflight
+  },
+  // ...其他配置
+}
+```
+
+### 平台差异处理
+
+**问题**: 某些API在不同平台行为不一致
+**解决方案**: 使用平台检测和条件渲染
+
+```tsx
+import { isWeapp, isH5 } from '@/utils/platform'
+
+export function PlatformSpecificComponent() {
+  return (
+    <View>
+      {isWeapp && <Text>微信小程序特有功能</Text>}
+      {isH5 && <Text>H5特有功能</Text>}
+    </View>
+  )
+}
+```
+
+### 性能优化
+
+**问题**: 页面加载性能问题
+**解决方案**:
+- 使用React.memo优化组件重渲染
+- 合理使用React Query缓存
+- 图片懒加载和压缩
+
+## 迁移检查清单
+
+### 页面迁移检查
+- [ ] 组件结构正确迁移
+- [ ] 样式正确转换
+- [ ] 事件处理正确绑定
+- [ ] 状态管理正确实现
+- [ ] 数据获取正确集成
+- [ ] 路由跳转正常工作
+- [ ] 平台兼容性测试通过
+
+### 功能验证检查
+- [ ] 用户交互流程正常
+- [ ] 数据展示正确
+- [ ] 错误处理完善
+- [ ] 加载状态处理
+- [ ] 空状态处理
+
+### 质量保证检查
+- [ ] 单元测试覆盖
+- [ ] 集成测试通过
+- [ ] E2E测试通过
+- [ ] 代码审查完成
+- [ ] 性能测试通过
+
+## 精确样式迁移指南
+
+## 设计系统核心规范
+
+### 颜色系统
+```css
+/* 主色调 */
+--primary-color: #4A90C2
+--primary-gradient: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%)
+--secondary-color: #5B9BD5
+
+/* 包车主题 */
+--charter-color: #d4af37
+--charter-gradient: linear-gradient(135deg, #d4af37 0%, #f4d03f 100%)
+--charter-bg: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%)
+
+/* 功能色 */
+--success-color: #52c41a
+--warning-color: #fa8c16
+--error-color: #ff4d4f
+--info-color: #1890ff
+
+/* 中性色 */
+--text-color: #333
+--text-secondary: #666
+--text-placeholder: #8E8E93
+--border-color: #E5E5EA
+--bg-color: #F8F9FA
+--card-bg: #FFFFFF
+```
+
+### 阴影系统
+```css
+--shadow-light: 0 2rpx 12rpx rgba(0, 0, 0, 0.08)
+--shadow-medium: 0 4rpx 20rpx rgba(0, 0, 0, 0.12)
+--shadow-heavy: 0 8rpx 32rpx rgba(0, 0, 0, 0.16)
+```
+
+## Tailwind CSS 精确配置
+
+### tailwind.config.js 扩展配置
+```javascript
+module.exports = {
+  theme: {
+    extend: {
+      colors: {
+        // 精确匹配 mini-demo 颜色
+        'primary': '#4A90C2',
+        'primary-dark': '#357ABD',
+        'secondary': '#5B9BD5',
+        'charter': '#d4af37',
+        'charter-light': '#f4d03f',
+        'charter-dark': '#1a1a1a',
+        'charter-bg': '#2d2d2d',
+        'success': '#52c41a',
+        'warning': '#fa8c16',
+        'error': '#ff4d4f',
+        'info': '#1890ff',
+        'text-primary': '#333',
+        'text-secondary': '#666',
+        'text-placeholder': '#8E8E93',
+        'border': '#E5E5EA',
+        'bg': '#F8F9FA',
+        'card': '#FFFFFF',
+      },
+      borderRadius: {
+        'card': '20rpx',
+        'button': '50rpx',
+        'small': '12rpx',
+        'medium': '16rpx',
+      },
+      boxShadow: {
+        'light': '0 2rpx 12rpx rgba(0, 0, 0, 0.08)',
+        'medium': '0 4rpx 20rpx rgba(0, 0, 0, 0.12)',
+        'heavy': '0 8rpx 32rpx rgba(0, 0, 0, 0.16)',
+        'primary': '0 8rpx 24rpx rgba(74, 144, 194, 0.4)',
+        'charter': '0 8rpx 24rpx rgba(212, 175, 55, 0.4)',
+      },
+      spacing: {
+        'card': '32rpx',
+        'section': '24rpx',
+        'button': '24rpx',
+      }
+    }
+  }
+}
+```
+
+## 样式迁移对照表
+
+### 通用样式类
+| 原WXSS类名 | Tailwind类名 | 说明 |
+|-----------|-------------|------|
+| `.card` | `bg-card rounded-card shadow-medium p-card border border-border` | 卡片样式 |
+| `.big-btn` | `bg-gradient-to-br from-primary to-primary-dark text-white rounded-button p-button shadow-primary` | 大按钮 |
+| `.text-primary` | `text-primary` | 主色调文字 |
+| `.text-charter` | `text-charter` | 包车主题文字 |
+
+### 首页样式迁移
+
+#### 顶部轮播图
+```tsx
+// 原WXSS
+.banner-container { height: 25vh; }
+.banner-swiper { width: 100%; height: 100%; }
+
+// Tailwind迁移
+<View className="h-[25vh] w-full">
+  <Swiper className="w-full h-full">
+    {/* 轮播内容 */}
+  </Swiper>
+</View>
+```
+
+#### 出行方式选择
+```tsx
+// 原WXSS
+.service-tabs {
+  background: rgba(255, 255, 255, 0.95);
+  margin: 12rpx 20rpx;
+  border-radius: 50rpx;
+  padding: 6rpx;
+}
+
+// Tailwind迁移
+<View className="bg-white/95 m-[12rpx] mx-[20rpx] rounded-button p-[6rpx]">
+  {/* tab内容 */}
+</View>
+```
+
+#### 地点选择器
+```tsx
+// 原WXSS
+.route-item {
+  background: #ffffff;
+  border-radius: 16rpx;
+  padding: 20rpx 16rpx;
+  border: 2rpx solid #e1e8ed;
+  min-height: 88rpx;
+}
+
+// Tailwind迁移
+<View className="bg-white rounded-medium p-[20rpx_16rpx] border-2 border-[#e1e8ed] min-h-[88rpx]">
+  {/* 地点选择内容 */}
+</View>
+```
+
+### 活动选择页面样式迁移
+
+#### 头部信息
+```tsx
+// 原WXSS
+.header-info {
+  background: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%);
+  padding: 40rpx 32rpx;
+  color: #fff;
+}
+
+// Tailwind迁移
+<View className="bg-gradient-to-br from-primary to-primary-dark p-[40rpx_32rpx] text-white">
+  {/* 头部内容 */}
+</View>
+```
+
+#### 行程区域
+```tsx
+// 原WXSS
+.trip-header.departure {
+  background: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%);
+  color: #fff;
+}
+
+// Tailwind迁移
+<View className="bg-gradient-to-br from-primary to-primary-dark text-white">
+  {/* 去程头部 */}
+</View>
+```
+
+### 班次列表页面样式迁移
+
+#### 包车卡片
+```tsx
+// 原WXSS
+.charter-card {
+  background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
+  border: 2rpx solid #d4af37;
+  box-shadow: 0 8rpx 32rpx rgba(212, 175, 55, 0.3);
+}
+
+// Tailwind迁移
+<View className="bg-gradient-to-br from-charter-dark to-charter-bg border-2 border-charter shadow-charter">
+  {/* 包车卡片内容 */}
+</View>
+```
+
+#### 预订按钮
+```tsx
+// 原WXSS
+.book-btn {
+  background: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%);
+  color: #fff;
+  border-radius: 50rpx;
+  padding: 24rpx 0;
+  box-shadow: 0 8rpx 24rpx rgba(74, 144, 194, 0.4);
+}
+
+// Tailwind迁移
+<Button className="bg-gradient-to-br from-primary to-primary-dark text-white rounded-button p-[24rpx_0] shadow-primary">
+  预订
+</Button>
+```
+
+### 订单页面样式迁移
+
+#### 包车主题
+```tsx
+// 原WXSS
+.charter-theme {
+  background: linear-gradient(180deg, #1a1a1a 0%, #2d2d2d 100%);
+}
+
+// Tailwind迁移
+<View className="bg-gradient-to-b from-charter-dark to-charter-bg">
+  {/* 包车主题内容 */}
+</View>
+```
+
+#### 支付区域
+```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迁移
+<View className="fixed bottom-0 left-0 right-0 bg-white p-[24rpx_32rpx] shadow-[0_-4rpx_20rpx_rgba(0,0,0,0.1)]">
+  {/* 支付内容 */}
+</View>
+```
+
+### 我的页面样式迁移
+
+#### 用户信息区域
+```tsx
+// 原WXSS
+.user-section {
+  background: linear-gradient(135deg, #4A90C2 0%, #357ABD 100%);
+  padding: 40rpx 32rpx 32rpx 32rpx;
+  color: #fff;
+}
+
+// Tailwind迁移
+<View className="bg-gradient-to-br from-primary to-primary-dark p-[40rpx_32rpx_32rpx_32rpx] text-white">
+  {/* 用户信息内容 */}
+</View>
+```
+
+#### 会员卡片
+```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迁移
+<View className="bg-gradient-to-br from-[#667eea] to-[#764ba2] rounded-card p-[24rpx] text-white shadow-[0_6rpx_24rpx_rgba(102,126,234,0.3)]">
+  {/* 会员卡片内容 */}
+</View>
+```
+
+## 响应式设计迁移
+
+### 媒体查询转换
+```tsx
+// 原WXSS
+@media screen and (max-height: 667px) {
+  .banner-container { height: 22vh; }
+  .booking-section { padding: 16rpx; }
+}
+
+// Tailwind迁移
+<View className="h-[25vh] sm:h-[22vh]">
+  {/* 响应式内容 */}
+</View>
+```
+
+## 动画效果迁移
+
+### 渐入动画
+```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迁移
+<View className="animate-[fadeIn_0.3s_ease-in-out]">
+  {/* 动画内容 */}
+</View>
+
+// 在全局CSS中添加
+@keyframes fadeIn {
+  from { opacity: 0; transform: translateY(20rpx); }
+  to { opacity: 1; transform: translateY(0); }
+}
+```
+
+### 视觉一致性检查清单
+- [ ] 颜色系统完全匹配
+- [ ] 渐变效果一致
+- [ ] 阴影层次相同
+- [ ] 圆角设计一致
+- [ ] 间距布局相同
+- [ ] 字体大小和权重一致
+- [ ] 按钮交互效果相同
+- [ ] 包车主题效果一致
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2025-11-15

+ 485 - 0
docs/architecture/tailwind-css-standards.md

@@ -0,0 +1,485 @@
+# Tailwind CSS 样式规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-10-15 | 初始Tailwind CSS样式规范 | Winston |
+
+## 概述
+
+本文档定义了出行服务项目中Tailwind CSS的使用规范和最佳实践,特别针对Taro小程序环境的适配和优化。
+
+## 技术栈配置
+
+### 核心依赖
+```json
+{
+  "tailwindcss": "^4.1.11",
+  "weapp-tailwindcss": "^4.2.5",
+  "@tailwindcss/postcss": "^4.1.11",
+  "autoprefixer": "^10.4.21",
+  "postcss": "^8.4.38"
+}
+```
+
+## 配置规范
+
+### Tailwind配置
+```javascript
+// tailwind.config.js
+const { iconsPlugin, getIconCollections } = require("@egoist/tailwindcss-icons")
+
+module.exports = {
+  content: [
+    './src/**/*.{html,js,ts,jsx,tsx}',
+  ],
+  theme: {
+    extend: {
+      colors: {
+        // 项目品牌色
+        primary: {
+          50: '#f0f9ff',
+          500: '#3b82f6',
+          600: '#2563eb',
+          700: '#1d4ed8',
+        },
+        // 语义化颜色
+        destructive: {
+          50: '#fef2f2',
+          500: '#ef4444',
+          600: '#dc2626',
+        },
+      },
+      spacing: {
+        '18': '4.5rem',
+        '88': '22rem',
+      },
+      borderRadius: {
+        '4xl': '2rem',
+      }
+    },
+  },
+  plugins: [
+    iconsPlugin({
+      collections: getIconCollections(["mdi", "lucide", "heroicons"]),
+    }),
+  ],
+  corePlugins: {
+    preflight: false, // 小程序环境禁用preflight
+  },
+}
+```
+
+### Webpack配置(小程序适配)
+```javascript
+// config/index.ts
+mini: {
+  webpackChain(chain) {
+    chain.merge({
+      plugin: {
+        install: {
+          plugin: UnifiedWebpackPluginV5,
+          args: [{
+            cssSelectorReplacement: {
+              universal: ['view','text','button', 'input']
+            },
+            cssChildCombinatorReplaceValue: ['view', 'text', 'button', 'input']
+          }]
+        }
+      }
+    })
+  }
+}
+```
+
+## 样式类名使用规范
+
+### 原子化类名原则
+
+**推荐做法:**
+```tsx
+// ✅ 使用原子化类名
+<View className="flex items-center justify-between p-4 bg-white rounded-lg shadow-sm">
+  <Text className="text-lg font-semibold text-gray-900">标题</Text>
+  <Button className="px-3 py-1 bg-blue-500 text-white rounded">按钮</Button>
+</View>
+```
+
+**不推荐做法:**
+```tsx
+// ❌ 避免自定义CSS类
+<View className="card-container">
+  <Text className="card-title">标题</Text>
+  <Button className="card-button">按钮</Button>
+</View>
+
+// 对应的CSS
+.card-container { /* ... */ }
+.card-title { /* ... */ }
+.card-button { /* ... */ }
+```
+
+### 响应式设计
+
+**移动端优先:**
+```tsx
+<View className="w-full md:w-1/2 lg:w-1/3">
+  <Text className="text-sm md:text-base lg:text-lg">响应式文本</Text>
+</View>
+```
+
+**小程序专用响应式:**
+```tsx
+<View className="w-full min-[375px]:w-11/12 min-[414px]:w-5/6">
+  <Text className="text-[32rpx] min-[375px]:text-[34rpx]">适配不同屏幕</Text>
+</View>
+```
+
+## 组件样式规范
+
+### shadcn/ui组件适配
+
+**按钮组件样式:**
+```tsx
+// src/components/ui/button.tsx
+const buttonVariants = cva(
+  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
+  {
+    variants: {
+      variant: {
+        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+        outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
+        secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+        ghost: 'hover:bg-accent hover:text-accent-foreground',
+        link: 'underline-offset-4 hover:underline text-primary',
+      },
+      size: {
+        default: 'h-10 py-2 px-4',
+        sm: 'h-9 px-3 rounded-md text-xs',
+        lg: 'h-11 px-8 rounded-md',
+        icon: 'h-10 w-10',
+      },
+    },
+    defaultVariants: {
+      variant: 'default',
+      size: 'default',
+    },
+  }
+)
+```
+
+### 表单组件样式
+
+**输入框组件:**
+```tsx
+// src/components/ui/input.tsx
+const inputVariants = cva(
+  'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
+  {
+    variants: {
+      variant: {
+        default: '',
+        search: 'pl-8',
+      },
+    },
+    defaultVariants: {
+      variant: 'default',
+    },
+  }
+)
+```
+
+## 布局系统规范
+
+### 容器布局
+
+**页面容器:**
+```tsx
+<View className="min-h-screen bg-gray-50">
+  {/* 安全区域适配 */}
+  <View className="safe-area">
+    {/* 内容容器 */}
+    <View className="container mx-auto px-4">
+      {/* 页面内容 */}
+    </View>
+  </View>
+</View>
+```
+
+**Flex布局:**
+```tsx
+<View className="flex flex-col space-y-4">
+  <View className="flex items-center justify-between">
+    <Text>左侧</Text>
+    <Text>右侧</Text>
+  </View>
+
+  <View className="flex-1">
+    <Text>自适应内容</Text>
+  </View>
+</View>
+```
+
+### Grid布局
+
+**网格系统:**
+```tsx
+<View className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+  <View className="bg-white p-4 rounded-lg shadow">卡片1</View>
+  <View className="bg-white p-4 rounded-lg shadow">卡片2</View>
+  <View className="bg-white p-4 rounded-lg shadow">卡片3</View>
+</View>
+```
+
+## 颜色系统规范
+
+### 语义化颜色
+
+**文本颜色:**
+```tsx
+<Text className="text-primary">主要文本</Text>
+<Text className="text-secondary">次要文本</Text>
+<Text className="text-muted-foreground">弱化文本</Text>
+<Text className="text-destructive">错误文本</Text>
+```
+
+**背景颜色:**
+```tsx
+<View className="bg-background">页面背景</View>
+<View className="bg-card">卡片背景</View>
+<View className="bg-popover">弹出层背景</View>
+<View className="bg-muted">弱化背景</View>
+```
+
+### 品牌颜色使用
+
+**主要品牌色:**
+```tsx
+<Button className="bg-primary text-primary-foreground">主要按钮</Button>
+<Text className="text-primary">品牌文字</Text>
+<View className="border-primary">品牌边框</View>
+```
+
+**状态颜色:**
+```tsx
+<View className="text-success">成功状态</View>
+<View className="text-warning">警告状态</View>
+<View className="text-error">错误状态</View>
+<View className="text-info">信息状态</View>
+```
+
+## 间距和尺寸规范
+
+### 间距系统
+
+**垂直间距:**
+```tsx
+<View className="space-y-2">  {/* 小间距 */}
+<View className="space-y-4">  {/* 中等间距 */}
+<View className="space-y-6">  {/* 大间距 */}
+<View className="space-y-8">  {/* 超大间距 */}
+```
+
+**水平间距:**
+```tsx
+<View className="space-x-2">  {/* 小间距 */}
+<View className="space-x-4">  {/* 中等间距 */}
+<View className="space-x-6">  {/* 大间距 */}
+```
+
+### 尺寸系统
+
+**固定尺寸:**
+```tsx
+<View className="w-16 h-16">     {/* 64px */}
+<View className="w-24 h-24">     {/* 96px */}
+<View className="w-32 h-32">     {/* 128px */}
+```
+
+**相对尺寸:**
+```tsx
+<View className="w-1/2">     {/* 50%宽度 */}
+<View className="w-full">    {/* 100%宽度 */}
+<View className="w-auto">    {/* 自动宽度 */}
+<View className="min-w-0">   {/* 最小宽度0 */}
+```
+
+## 字体和排版规范
+
+### 字体大小
+
+**文本层级:**
+```tsx
+<Text className="text-xs">  辅助文本 (12px)</Text>
+<Text className="text-sm">  小文本 (14px)</Text>
+<Text className="text-base">基础文本 (16px)</Text>
+<Text className="text-lg">  大文本 (18px)</Text>
+<Text className="text-xl">  标题文本 (20px)</Text>
+<Text className="text-2xl"> 大标题 (24px)</Text>
+```
+
+### 字体粗细
+
+**字重层级:**
+```tsx
+<Text className="font-normal">正常</Text>
+<Text className="font-medium">中等</Text>
+<Text className="font-semibold">半粗</Text>
+<Text className="font-bold">粗体</Text>
+```
+
+### 行高和字距
+
+**行高控制:**
+```tsx
+<Text className="leading-tight">   紧密行高</Text>
+<Text className="leading-normal"> 正常行高</Text>
+<Text className="leading-relaxed">宽松行高</Text>
+<Text className="leading-loose">  超松行高</Text>
+```
+
+## 交互状态规范
+
+### 悬停状态
+
+**按钮悬停:**
+```tsx
+<Button className="bg-primary hover:bg-primary/90">
+  悬停变暗
+</Button>
+```
+
+**卡片悬停:**
+```tsx
+<View className="bg-white hover:bg-gray-50 transition-colors">
+  悬停变亮
+</View>
+```
+
+### 焦点状态
+
+**输入框焦点:**
+```tsx
+<Input className="focus:ring-2 focus:ring-primary focus:border-transparent" />
+```
+
+**按钮焦点:**
+```tsx
+<Button className="focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2">
+  焦点环
+</Button>
+```
+
+### 激活状态
+
+**按钮激活:**
+```tsx
+<Button className="active:scale-95 transition-transform">
+  点击缩放
+</Button>
+```
+
+## 动画和过渡规范
+
+### 过渡动画
+
+**基础过渡:**
+```tsx
+<View className="transition-all duration-300 ease-in-out">
+  所有属性过渡
+</View>
+
+<View className="transition-colors duration-200">
+  颜色过渡
+</View>
+
+<View className="transition-transform duration-150">
+  变换过渡
+</View>
+```
+
+### 变换动画
+
+**缩放变换:**
+```tsx
+<View className="hover:scale-105 active:scale-95 transition-transform">
+  悬停放大,点击缩小
+</View>
+```
+
+**位移变换:**
+```tsx
+<View className="hover:-translate-y-1 transition-transform">
+  悬停上移
+</View>
+```
+
+## 小程序特殊适配
+
+### rpx单位使用
+
+**响应式单位:**
+```tsx
+<View className="w-[750rpx] h-[200rpx]">
+  <Text className="text-[32rpx]">使用rpx单位</Text>
+</View>
+```
+
+### 安全区域适配
+
+**iPhone安全区域:**
+```tsx
+<View className="pb-safe">
+  {/* 底部安全区域 */}
+</View>
+
+<View className="pt-safe">
+  {/* 顶部安全区域 */}
+</View>
+```
+
+## 性能优化规范
+
+### 类名合并
+
+**使用cn工具:**
+```tsx
+import { cn } from '@/utils/cn'
+
+export function Component({ className, variant }) {
+  return (
+    <View className={cn(
+      'base-classes',
+      variant === 'primary' && 'primary-classes',
+      className
+    )}>
+      内容
+    </View>
+  )
+}
+```
+
+### 避免样式冲突
+
+**作用域样式:**
+```tsx
+// 使用特定的前缀或命名空间
+<View className="travel-home-page">
+  <Text className="travel-home-title">标题</Text>
+</View>
+```
+
+## 最佳实践总结
+
+1. **原子化优先**: 优先使用Tailwind原子类,避免自定义CSS
+2. **语义化命名**: 使用有意义的颜色和尺寸命名
+3. **响应式设计**: 移动端优先,逐步增强
+4. **一致性**: 保持整个项目的样式一致性
+5. **性能**: 合理使用类名合并,避免样式冗余
+6. **可维护性**: 建立统一的样式规范和组件库
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2025-11-15

+ 634 - 0
docs/architecture/taro-mini-program-standards.md

@@ -0,0 +1,634 @@
+# Taro 小程序开发规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-10-15 | 初始Taro小程序开发规范 | Winston |
+
+## 概述
+
+本文档定义了出行服务项目中Taro小程序开发的规范和最佳实践,确保代码质量、一致性和可维护性。
+
+## 技术栈配置
+
+### 核心依赖版本
+```json
+{
+  "@tarojs/taro": "4.1.4",
+  "@tarojs/react": "4.1.4",
+  "react": "^18.0.0",
+  "tailwindcss": "^4.1.11",
+  "weapp-tailwindcss": "^4.2.5"
+}
+```
+
+### 多平台编译配置
+```typescript
+// config/index.ts
+const platform = process.env.TARO_ENV || 'weapp'
+const env = process.env.NODE_ENV || 'development'
+const outputDir = `dist/${platform}/${env}`
+```
+
+## 项目结构规范
+
+### 目录结构
+```
+mini/
+├── src/
+│   ├── pages/              # 页面组件
+│   │   ├── index/         # 首页
+│   │   ├── login/         # 登录页
+│   │   └── profile/       # 个人中心
+│   ├── components/        # 共享组件
+│   │   └── ui/           # shadcn/ui组件库
+│   ├── layouts/          # 布局组件
+│   ├── utils/            # 工具函数
+│   │   ├── auth.tsx      # 认证工具
+│   │   ├── cn.ts         # className工具
+│   │   └── platform.ts   # 平台检测
+│   ├── schemas/          # Zod验证schema
+│   └── app.tsx           # 应用入口
+├── config/
+│   ├── index.ts          # 主配置
+│   ├── dev.ts            # 开发配置
+│   └── prod.ts           # 生产配置
+└── tailwind.config.js    # Tailwind配置
+```
+
+## 组件开发规范
+
+### Taro组件适配
+
+**基础组件映射:**
+- `View` → `@tarojs/components`
+- `Text` → `@tarojs/components`
+- `Button` → `@tarojs/components`
+- `Input` → `@tarojs/components`
+
+**组件封装示例:**
+```typescript
+// src/components/ui/button.tsx
+import { Button as TaroButton, ButtonProps as TaroButtonProps } from '@tarojs/components'
+import { cn } from '@/utils/cn'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+const buttonVariants = cva(
+  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors',
+  {
+    variants: {
+      variant: {
+        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+        outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
+      },
+      size: {
+        default: 'h-10 py-2 px-4',
+        sm: 'h-9 px-3 rounded-md text-xs',
+        lg: 'h-11 px-8 rounded-md',
+      },
+    },
+    defaultVariants: {
+      variant: 'default',
+      size: 'default',
+    },
+  }
+)
+
+interface ButtonProps extends Omit<TaroButtonProps, 'size'>, VariantProps<typeof buttonVariants> {
+  className?: string
+  children?: React.ReactNode
+}
+
+export function Button({ className, variant, size, ...props }: ButtonProps) {
+  return (
+    <TaroButton
+      className={cn(buttonVariants({ variant, size, className }))}
+      {...props}
+    />
+  )
+}
+```
+
+### 页面组件规范
+
+**页面结构:**
+```typescript
+// src/pages/index/index.tsx
+import { View, Text } from '@tarojs/components'
+import { useLoad } from '@tarojs/taro'
+import { Button } from '@/components/ui/button'
+
+export default function IndexPage() {
+  useLoad(() => {
+    console.log('Page loaded.')
+  })
+
+  return (
+    <View className="min-h-screen bg-background">
+      <View className="container mx-auto px-4">
+        <Text className="text-2xl font-bold">首页</Text>
+        <Button variant="default">点击按钮</Button>
+      </View>
+    </View>
+  )
+}
+```
+
+**页面配置:**
+```typescript
+// src/pages/index/index.config.ts
+export default definePageConfig({
+  navigationBarTitleText: '首页',
+  enableShareAppMessage: true,
+  usingComponents: {}
+})
+```
+
+## 状态管理规范
+
+### React Query配置
+```typescript
+// src/utils/auth.tsx
+import { QueryClient } from '@tanstack/react-query'
+
+export const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      retry: 1,
+      staleTime: 5 * 60 * 1000, // 5分钟
+    },
+  },
+})
+```
+
+### 数据获取示例
+```typescript
+import { useQuery } from '@tanstack/react-query'
+import { api } from '@/utils/api'
+
+export function useRoutes(params: RouteQueryParams) {
+  return useQuery({
+    queryKey: ['routes', params],
+    queryFn: () => api.routes.list(params),
+    enabled: !!params.startPoint && !!params.endPoint,
+  })
+}
+```
+
+## 路由和导航规范
+
+### 页面跳转
+```typescript
+import { navigateTo, switchTab, redirectTo } from '@tarojs/taro'
+
+// 普通页面跳转
+navigateTo({
+  url: '/pages/schedule-list/index'
+})
+
+// Tab页面跳转
+switchTab({
+  url: '/pages/index/index'
+})
+
+// 重定向
+redirectTo({
+  url: '/pages/login/index'
+})
+```
+
+### 路由参数处理
+```typescript
+import { useRouter } from '@tarojs/taro'
+
+export default function ScheduleListPage() {
+  const router = useRouter()
+  const { startPoint, endPoint, date } = router.params
+
+  // 参数验证和类型转换
+  const queryParams = {
+    startPoint: startPoint || '',
+    endPoint: endPoint || '',
+    date: date ? new Date(date) : new Date()
+  }
+}
+```
+
+## 平台适配规范
+
+### 平台检测
+```typescript
+// src/utils/platform.ts
+export const isWeapp = process.env.TARO_ENV === 'weapp'
+export const isH5 = process.env.TARO_ENV === 'h5'
+export const isAlipay = process.env.TARO_ENV === 'alipay'
+
+export function usePlatform() {
+  return {
+    isWeapp,
+    isH5,
+    isAlipay,
+    platform: process.env.TARO_ENV || 'weapp'
+  }
+}
+```
+
+### 条件渲染
+```typescript
+import { View, Text } from '@tarojs/components'
+import { isWeapp, isH5 } from '@/utils/platform'
+
+export function PlatformSpecificComponent() {
+  return (
+    <View>
+      {isWeapp && <Text>微信小程序特有内容</Text>}
+      {isH5 && <Text>H5特有内容</Text>}
+      <Text>通用内容</Text>
+    </View>
+  )
+}
+```
+
+## 小程序API使用规范
+
+### 授权和权限
+```typescript
+import { authorize, getSetting } from '@tarojs/taro'
+
+export async function requestUserLocation() {
+  try {
+    const { authSetting } = await getSetting()
+
+    if (!authSetting['scope.userLocation']) {
+      await authorize({ scope: 'scope.userLocation' })
+    }
+
+    // 获取位置信息
+    const { latitude, longitude } = await getLocation()
+    return { latitude, longitude }
+  } catch (error) {
+    console.error('获取位置失败:', error)
+    throw error
+  }
+}
+```
+
+### 文件上传
+```typescript
+import { chooseImage, uploadFile } from '@tarojs/taro'
+
+export async function uploadAvatar() {
+  try {
+    const { tempFilePaths } = await chooseImage({
+      count: 1,
+      sizeType: ['compressed'],
+      sourceType: ['album', 'camera']
+    })
+
+    const uploadResult = await uploadFile({
+      url: `${API_BASE}/files/upload`,
+      filePath: tempFilePaths[0],
+      name: 'file',
+      header: {
+        'Authorization': `Bearer ${token}`
+      }
+    })
+
+    return JSON.parse(uploadResult.data)
+  } catch (error) {
+    console.error('上传失败:', error)
+    throw error
+  }
+}
+```
+
+## 性能优化规范
+
+### 图片优化
+```typescript
+import { Image } from '@tarojs/components'
+
+export function OptimizedImage({ src, alt, className }) {
+  return (
+    <Image
+      src={src}
+      mode="aspectFill"
+      lazyLoad
+      className={className}
+      onError={(e) => {
+        console.warn('图片加载失败:', src)
+        // 设置默认图片
+      }}
+    />
+  )
+}
+```
+
+### 列表优化
+```typescript
+import { VirtualList } from '@tarojs/components'
+
+export function RouteList({ routes }) {
+  return (
+    <VirtualList
+      height={500}
+      width="100%"
+      itemData={routes}
+      itemCount={routes.length}
+      itemSize={100}
+    >
+      {({ index, data }) => (
+        <RouteItem route={data[index]} />
+      )}
+    </VirtualList>
+  )
+}
+```
+
+## 错误处理规范
+
+### 全局错误处理
+```typescript
+// src/app.tsx
+import { useEffect } from 'react'
+import { useError } from '@tarojs/taro'
+
+export default function App({ children }) {
+  useError((error) => {
+    console.error('全局错误:', error)
+    // 上报错误到监控系统
+  })
+
+  return children
+}
+```
+
+### API错误处理
+```typescript
+export async function apiCall<T>(fn: () => Promise<T>): Promise<T> {
+  try {
+    return await fn()
+  } catch (error) {
+    if (error.status === 401) {
+      // 跳转到登录页
+      redirectTo({ url: '/pages/login/index' })
+    }
+    throw error
+  }
+}
+```
+
+## API调用规范
+
+### RPC客户端使用
+
+**基础API调用:**
+```typescript
+import { userClient } from '@/api'
+
+export async function fetchUsers(params: UserQueryParams) {
+  try {
+    const response = await userClient.$get({
+      query: {
+        page: params.page || 1,
+        pageSize: params.pageSize || 10,
+        keyword: params.keyword,
+        sortBy: params.sortBy,
+        sortOrder: params.sortOrder
+      }
+    })
+
+    if (!response.ok) {
+      throw new Error(`API Error: ${response.status}`)
+    }
+
+    return await response.json()
+  } catch (error) {
+    console.error('获取用户列表失败:', error)
+    throw error
+  }
+}
+```
+
+**带认证的API调用:**
+```typescript
+import { authClient } from '@/api'
+
+export async function login(username: string, password: string) {
+  try {
+    const response = await authClient.login.$post({
+      json: { username, password }
+    })
+
+    if (!response.ok) {
+      throw new Error(`登录失败: ${response.status}`)
+    }
+
+    const result = await response.json()
+
+    // 存储token
+    Taro.setStorageSync('mini_token', result.token)
+
+    return result
+  } catch (error) {
+    console.error('登录失败:', error)
+    throw error
+  }
+}
+```
+
+### 文件上传规范
+
+**使用统一上传接口:**
+```typescript
+import { uploadFromSelect } from '@/utils/minio'
+
+export async function uploadAvatar() {
+  try {
+    const result = await uploadFromSelect('avatars', {
+      sourceType: ['album'],
+      count: 1,
+      accept: 'image/*',
+      maxSize: 5 * 1024 * 1024 // 5MB
+    }, {
+      onProgress: (event) => {
+        console.log(`上传进度: ${event.progress}%`)
+        // 更新UI进度条
+      },
+      onComplete: () => {
+        console.log('上传完成')
+        Taro.showToast({ title: '上传成功', icon: 'success' })
+      },
+      onError: (error) => {
+        console.error('上传失败:', error)
+        Taro.showToast({ title: '上传失败', icon: 'none' })
+      }
+    })
+
+    return result.fileUrl
+  } catch (error) {
+    console.error('文件选择或上传失败:', error)
+    throw error
+  }
+}
+```
+
+**手动文件上传:**
+```typescript
+import { uploadMinIOWithPolicy } from '@/utils/minio'
+
+export async function uploadFileManually(filePath: string, fileName: string) {
+  try {
+    const result = await uploadMinIOWithPolicy(
+      'documents', // 上传路径
+      filePath,    // 文件路径(小程序)或File对象(H5)
+      fileName,    // 文件名
+      {
+        onProgress: (event) => {
+          console.log(`上传进度: ${event.progress}%`)
+        }
+      }
+    )
+
+    return result
+  } catch (error) {
+    console.error('文件上传失败:', error)
+    throw error
+  }
+}
+```
+
+### 错误处理规范
+
+**统一错误处理:**
+```typescript
+export const handleApiError = (error: any) => {
+  console.error('API错误:', error)
+
+  if (error.status === 401) {
+    // 未授权,跳转到登录页
+    Taro.redirectTo({ url: '/pages/login/index' })
+    Taro.showToast({ title: '请重新登录', icon: 'none' })
+  } else if (error.status === 403) {
+    // 权限不足
+    Taro.showToast({ title: '权限不足', icon: 'none' })
+  } else if (error.status === 404) {
+    // 资源不存在
+    Taro.showToast({ title: '资源不存在', icon: 'none' })
+  } else if (error.status >= 500) {
+    // 服务器错误
+    Taro.showToast({ title: '服务器错误,请稍后重试', icon: 'none' })
+  } else {
+    // 其他错误
+    Taro.showToast({ title: error.message || '网络错误', icon: 'none' })
+  }
+}
+```
+
+**React Query错误处理:**
+```typescript
+import { useQuery } from '@tanstack/react-query'
+import { handleApiError } from '@/utils/error-handler'
+
+export function useUserProfile(userId: number) {
+  return useQuery({
+    queryKey: ['user', userId],
+    queryFn: async () => {
+      const response = await userClient[':id'].$get({ param: { id: userId.toString() } })
+      if (!response.ok) {
+        throw new Error(`获取用户信息失败: ${response.status}`)
+      }
+      return response.json()
+    },
+    onError: handleApiError,
+    retry: 1,
+    staleTime: 5 * 60 * 1000 // 5分钟缓存
+  })
+}
+```
+
+## 测试规范
+
+### 单元测试
+```typescript
+// tests/unit/components/button.test.tsx
+import { render } from '@testing-library/react'
+import { Button } from '@/components/ui/button'
+
+describe('Button', () => {
+  it('renders with default variant', () => {
+    const { getByText } = render(<Button>点击我</Button>)
+    expect(getByText('点击我')).toBeInTheDocument()
+  })
+})
+```
+
+### API测试
+```typescript
+// tests/unit/api/user-api.test.ts
+import { userClient } from '@/api'
+
+describe('User API', () => {
+  it('should fetch user list', async () => {
+    const response = await userClient.$get({
+      query: { page: 1, pageSize: 10 }
+    })
+
+    expect(response.ok).toBe(true)
+    const data = await response.json()
+    expect(data).toHaveProperty('data')
+    expect(data).toHaveProperty('pagination')
+  })
+})
+```
+
+### E2E测试
+```typescript
+// tests/e2e/travel-flow.spec.ts
+test('完整的出行查询流程', async ({ page }) => {
+  await page.goto('/pages/index/index')
+  await page.fill('[data-testid="start-point"]', '北京')
+  await page.fill('[data-testid="end-point"]', '上海')
+  await page.click('[data-testid="search-button"]')
+
+  await expect(page.locator('[data-testid="route-list"]')).toBeVisible()
+})
+```
+
+## 部署和发布规范
+
+### 构建命令
+```bash
+# 微信小程序开发环境
+pnpm dev:weapp
+
+# 微信小程序生产构建
+pnpm build:weapp
+
+# H5开发环境
+pnpm dev:h5
+
+# H5生产构建
+pnpm build:h5
+```
+
+### 版本管理
+- 使用语义化版本控制
+- 每次发布前更新版本号
+- 维护CHANGELOG.md记录变更
+
+## 最佳实践总结
+
+1. **组件设计**: 优先使用函数组件和Hooks
+2. **状态管理**: 使用React Query管理服务端状态
+3. **样式处理**: 统一使用Tailwind CSS + shadcn/ui
+4. **类型安全**: 全面使用TypeScript
+5. **错误处理**: 统一的错误处理机制
+6. **性能优化**: 合理使用懒加载和虚拟列表
+7. **测试覆盖**: 编写全面的单元测试和E2E测试
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2025-11-15

+ 5 - 0
docs/stories/005.001.story.md

@@ -104,6 +104,11 @@ Draft
 - **状态管理**: React Query [Source: architecture/tech-stack.md#现有技术栈维护]
 - **样式**: Tailwind CSS [Source: architecture/tech-stack.md#现有技术栈维护]
 
+### 开发规范要求
+- **Taro开发**: 遵循 [Taro小程序开发规范](../architecture/taro-mini-program-standards.md)
+- **样式开发**: 遵循 [Tailwind CSS样式规范](../architecture/tailwind-css-standards.md)
+- **迁移指导**: 参考 [mini-demo迁移指导规范](../architecture/mini-demo-migration-guide.md)(包含精确样式迁移)
+
 ### 通用CRUD使用模式
 - **路由创建**: 使用 `createCrudRoutes` 函数创建标准CRUD路由 [Source: src/server/api/users/index.ts#L9]
 - **搜索配置**: 配置 `searchFields` 支持关键词搜索 [Source: src/server/api/users/index.ts#L15]