跳到主要内容

App 系统架构设计

· 阅读需 14 分钟
Quany
软件工程师

1. 概述

本项目是一个基于 React Native 的移动端销售管理系统,模仿纷享销客 App 的功能和界面设计,主要服务于企业销售团队的日常管理工作。

1.1 技术栈

类别技术选型版本
框架React Native0.82.1
语言TypeScript5.8.x
状态管理Zustand5.x
导航React Navigation7.x
UI 组件库@rneui/themed4.x
样式方案NativeWind (Tailwind)4.x
HTTP 客户端Axios1.7.x
表单处理Formik2.x
国际化react-i18next16.x
存储AsyncStorage + Keychain-
热更新CodePush-

2. 整体架构图

┌─────────────────────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Navigation (React Navigation) ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ HomeTabs │ │ Login │ │ WebView │ │ Setting │ ││
│ │ │ (企信/工作台│ │ Register │ │ Browser │ │ Profile │ ││
│ │ │ /CRM/我) │ │ │ │ │ │ │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ UI Components ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││
│ │ │ Common │ │ Form │ │ Message │ │ Layout │ │ Business │ ││
│ │ │Components│ │Components│ │Components│ │Components│ │Components│ ││
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ Business Layer │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ State Management ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ AuthStore │ │ ChatStore │ │ UserStore │ │ BusinessStore│ ││
│ │ │ (认证状态) │ │ (聊天状态) │ │ (用户状态) │ │ (业务状态) │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Services ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ AuthService │ │ UserService │ │OrderService │ │CacheService │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ API Client ││
│ │ ┌───────────────────────────────────────────────────────────────────┐ ││
│ │ │ Axios Instance (拦截器、Token管理、错误处理、Loading状态) │ ││
│ │ └───────────────────────────────────────────────────────────────────┘ ││
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││
│ │ │OrderAPI │ │PartnerAPI││CustomerAPI││ApprovalAPI││EmployeeAPI│ ││
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Local Storage ││
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ││
│ │ │ AsyncStorage │ │ Keychain │ │ react-native- │ ││
│ │ │ (用户偏好/缓存) │ │ (安全凭证存储) │ │ storage │ ││
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Theme │ │ i18n │ │ Config │ │ Utils │ │ Types │ │
│ │ (主题) │ │ (国际化) │ │ (配置) │ │ (工具) │ │ (类型) │ │
│ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

3. 目录结构设计

src/
├── api/ # API 层
│ ├── client.ts # Axios 客户端配置
│ ├── index.ts # API 统一导出
│ ├── sales.ts # 销售相关 API
│ ├── customer.ts # 客户相关 API (建议拆分)
│ ├── order.ts # 订单相关 API (建议拆分)
│ └── types/ # API 请求/响应类型定义
│ ├── request.ts
│ └── response.ts

├── components/ # 通用组件
│ ├── common/ # 基础通用组件
│ │ ├── Button/
│ │ ├── Card/
│ │ ├── Empty/
│ │ ├── ErrorBoundary/
│ │ └── index.ts
│ │
│ ├── form/ # 表单组件
│ │ ├── FormInput.tsx
│ │ ├── PasswordInput.tsx
│ │ ├── PhoneInput.tsx
│ │ ├── AgreementCheckbox.tsx
│ │ └── index.ts
│ │
│ ├── layout/ # 布局组件
│ │ ├── Header/
│ │ ├── TabBar/
│ │ ├── SafeContainer/
│ │ └── index.ts
│ │
│ ├── business/ # 业务组件
│ │ ├── CustomerCard/
│ │ ├── OrderItem/
│ │ ├── ApprovalCard/
│ │ └── index.ts
│ │
│ └── message/ # 消息相关组件
│ ├── MessageItem/
│ ├── ChatBubble/
│ └── index.ts

├── screens/ # 页面/屏幕
│ ├── user/ # 用户相关页面
│ │ ├── Login.tsx
│ │ ├── Register.tsx
│ │ └── Profile.tsx
│ │
│ ├── home/ # 主页 Tab 页面
│ │ ├── index.tsx # Tab 导航配置
│ │ └── tabs/
│ │ ├── CorporateMessage.tsx # 企信
│ │ ├── Workbench.tsx # 工作台
│ │ ├── Crm.tsx # CRM
│ │ └── My.tsx # 我
│ │
│ ├── crm/ # CRM 模块页面
│ │ ├── CustomerList/
│ │ ├── CustomerDetail/
│ │ ├── OrderList/
│ │ └── OrderDetail/
│ │
│ ├── approval/ # 审批模块页面
│ │ ├── ApprovalList/
│ │ └── ApprovalDetail/
│ │
│ └── common/ # 通用页面
│ ├── Setting.tsx
│ ├── SplashScreen.tsx
│ ├── PDFViewer.tsx
│ └── web/
│ └── index.tsx

├── navigation/ # 导航配置
│ ├── index.tsx # 根导航器
│ ├── types.ts # 导航类型定义
│ ├── linking.ts # 深度链接配置
│ └── helpers.ts # 导航辅助函数

├── store/ # 状态管理 (Zustand)
│ ├── index.ts # Store 工具函数
│ ├── authStore.ts # 认证状态
│ ├── userStore.ts # 用户状态
│ ├── chatStore.ts # 聊天状态
│ └── types.ts # Store 类型定义

├── services/ # 业务服务层
│ ├── authService.ts # 认证服务
│ ├── userService.ts # 用户服务
│ ├── storageService.ts # 存储服务
│ └── pushService.ts # 推送服务

├── hooks/ # 自定义 Hooks
│ ├── useAuth.ts
│ ├── useUser.ts
│ ├── useToast.ts
│ ├── useRefresh.ts
│ └── index.ts

├── contexts/ # React Context
│ ├── AuthContext.tsx
│ ├── UserContext.tsx
│ ├── ChatContext.tsx
│ └── index.ts

├── theme/ # 主题配置
│ ├── index.ts # 主题导出
│ ├── colors.ts # 颜色定义
│ ├── typography.ts # 字体定义
│ ├── spacing.ts # 间距定义
│ ├── shadows.ts # 阴影定义
│ └── ThemeContext.tsx # 主题 Context

├── constants/ # 常量定义
│ ├── config.ts # 应用配置
│ ├── sizes.ts # 尺寸常量
│ ├── api.ts # API 相关常量
│ └── index.ts

├── types/ # 全局类型定义
│ ├── api.ts # API 类型
│ ├── navigation.ts # 导航类型
│ ├── models/ # 数据模型类型
│ │ ├── user.ts
│ │ ├── customer.ts
│ │ ├── order.ts
│ │ └── index.ts
│ └── index.ts

├── utils/ # 工具函数
│ ├── env.ts # 环境变量工具
│ ├── storage.ts # 存储工具
│ ├── timezone.ts # 时区工具
│ ├── toast.ts # Toast 工具
│ ├── format.ts # 格式化工具
│ ├── validation.ts # 验证工具
│ └── index.ts

├── locales/ # 国际化资源
│ ├── en.json
│ ├── zh.json
│ └── index.ts

└── index.tsx # 应用入口

4. 分层架构详解

4.1 表现层 (Presentation Layer)

负责 UI 渲染和用户交互。

// screens/crm/CustomerList/index.tsx
import { useCustomerList } from '@/hooks/useCustomer';
import { CustomerCard } from '@/components/business';
import { SafeContainer, EmptyState } from '@/components/layout';

export default function CustomerList() {
const { data, loading, refresh } = useCustomerList();

if (loading) return <Loading />;
if (!data?.length) return <EmptyState type="customer" />;

return (
<SafeContainer>
<FlatList
data={data}
renderItem={({ item }) => <CustomerCard customer={item} />}
onRefresh={refresh}
/>
</SafeContainer>
);
}

4.2 业务逻辑层 (Business Layer)

4.2.1 状态管理 (Zustand Store)

// store/authStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import AsyncStorage from '@react-native-async-storage/async-storage';

interface AuthState {
isAuthenticated: boolean;
token: string | null;
user: UserInfo | null;
}

interface AuthActions {
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
refreshToken: () => Promise<void>;
setUser: (user: UserInfo) => void;
}

export const useAuthStore = create<AuthState & AuthActions>()(
persist(
immer((set, get) => ({
// State
isAuthenticated: false,
token: null,
user: null,

// Actions
async login(email: string, password: string) {
const { token, user } = await authService.login(email, password);
set(state => {
state.isAuthenticated = true;
state.token = token;
state.user = user;
});
},

async logout() {
await authService.logout();
set(state => {
state.isAuthenticated = false;
state.token = null;
state.user = null;
});
},

async refreshToken() {
const newToken = await authService.refreshToken(get().token);
set(state => {
state.token = newToken;
});
},

setUser(user: UserInfo) {
set(state => {
state.user = user;
});
},
})),
{
name: 'auth-storage',
storage: createJSONStorage(() => AsyncStorage),
partialize: (state) => ({ token: state.token, user: state.user }),
}
)
);

4.2.2 服务层 (Services)

// services/authService.ts
import * as Keychain from 'react-native-keychain';
import client from '@/api/client';
import Storage from '@/utils/storage';

export interface AuthService {
login(email: string, password: string): Promise<LoginResult>;
logout(): Promise<void>;
refreshToken(token: string | null): Promise<string>;
validateToken(): Promise<boolean>;
}

export const authService: AuthService = {
async login(email: string, password: string) {
// 1. 验证凭证
const validateRes = await client.post('/login/validate', { email, password });

// 2. 获取 Token
const loginRes = await client.post('/login/email', {
...validateRes.data,
email,
});

// 3. 安全存储 Token
await Keychain.setGenericPassword('auth', loginRes.token);

// 4. 存储用户信息
await Storage.save({
key: 'userInfo',
data: loginRes.user,
});

return loginRes;
},

async logout() {
await Keychain.resetGenericPassword();
await Storage.remove({ key: 'userInfo' });
},

async refreshToken(token) {
const res = await client.post('/auth/refresh', { token });
await Keychain.setGenericPassword('auth', res.token);
return res.token;
},

async validateToken() {
try {
const credentials = await Keychain.getGenericPassword();
if (!credentials) return false;

const res = await client.post('/auth/validate');
return res.valid === true;
} catch {
return false;
}
},
};

4.3 数据层 (Data Layer)

4.3.1 API 客户端

// api/client.ts
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
import Config from 'react-native-config';
import * as Keychain from 'react-native-keychain';
import { Loader } from '@/components/loading';
import { showToast } from '@/utils/toast';
import { reset } from '@/navigation';

// 创建实例
const client: AxiosInstance = axios.create({
baseURL: Config.SALES_SERVER,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});

// 请求拦截器
client.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
// 1. 处理 Loading
if (!config.hideLoading) {
Loader.current?.show();
}

// 2. 注入 Token
const credentials = await Keychain.getGenericPassword();
if (credentials?.password) {
config.headers['vulcan-token'] = credentials.password;
}

// 3. 注入通用请求头
config.headers['accept-timezone'] = await getTimezone();
config.headers['language-code'] = 'zh-CN';

return config;
},
(error) => Promise.reject(error)
);

// 响应拦截器
client.interceptors.response.use(
(response) => {
Loader.current?.hide();

const { code, data, msg } = response.data;

if (code === 200) {
return data;
}

// 业务错误处理
if (code === 401) {
showToast('登录已过期');
reset({ index: 0, routes: [{ name: 'Login' }] });
} else {
showToast(msg || '请求失败');
}

return Promise.reject(response.data);
},
(error) => {
Loader.current?.hide();

if (error.response?.status === 401) {
reset({ index: 0, routes: [{ name: 'Login' }] });
}

showToast(error.message || '网络错误');
return Promise.reject(error);
}
);

export default client;

4.3.2 API 模块化

// api/customer.ts
import client from './client';
import type { Customer, CustomerListParams, CustomerListResponse } from '@/types/models';

export const customerApi = {
/**
* 获取客户列表
*/
list(params: CustomerListParams): Promise<CustomerListResponse> {
return client.post('/web/customer/list', params);
},

/**
* 获取客户详情
*/
detail(customerId: string): Promise<Customer> {
return client.post('/web/customer/info', { customerId });
},

/**
* 创建/更新客户
*/
save(data: Partial<Customer>): Promise<Customer> {
return client.post('/web/customerManagement/createOrModifyCustomer', data);
},

/**
* 今日统计
*/
todayStatistics(): Promise<CustomerStatistics> {
return client.get('/web/customer/todayStatistics');
},
};

5. 导航架构

5.1 导航结构图

RootNavigator (Native Stack)
├── SplashScreen # 启动屏
├── AuthNavigator (Stack) # 认证流程
│ ├── Login # 登录
│ └── Register # 注册
├── MainNavigator (Stack) # 主应用
│ ├── HomeTabs (Bottom Tab) # 主页 Tabs
│ │ ├── CorporateMessage # 企信
│ │ ├── Workbench # 工作台
│ │ ├── CRM # CRM
│ │ └── My # 我
│ ├── CRMNavigator (Stack) # CRM 模块
│ │ ├── CustomerList
│ │ ├── CustomerDetail
│ │ ├── OrderList
│ │ └── OrderDetail
│ ├── ApprovalNavigator (Stack) # 审批模块
│ │ ├── ApprovalList
│ │ └── ApprovalDetail
│ └── CommonScreens # 通用页面
│ ├── Setting
│ ├── WebView
│ └── PDFViewer
└── Modal Screens # 模态页面
├── ImageViewer
└── ActionSheet

5.2 导航类型定义

// navigation/types.ts
import type { NavigatorScreenParams } from '@react-navigation/native';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';

// Root Navigator
export type RootStackParamList = {
SplashScreen: undefined;
Login: undefined;
Register: undefined;
HomeTabs: NavigatorScreenParams<HomeTabParamList>;
CustomerDetail: { customerId: string };
OrderDetail: { orderId: string };
Setting: undefined;
WebView: { url: string; title?: string };
};

// Home Tab Navigator
export type HomeTabParamList = {
CorporateMessage: undefined;
Workbench: undefined;
CRM: undefined;
My: undefined;
};

// Screen Props 类型
export type RootStackScreenProps<T extends keyof RootStackParamList> =
NativeStackScreenProps<RootStackParamList, T>;

// 扩展全局导航类型
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}

6. 数据流设计

6.1 单向数据流

┌─────────────────────────────────────────────────────────────────┐
│ 用户交互 │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ UI Component │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Props │ ← │ Store │ ← │ Service │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Action Dispatch │ │
│ │ onClick → store.action() → API Call → Update State │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ State Update │ │
│ │ Zustand: set(state => { state.xxx = newValue }) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Re-render │ │
│ │ UI Component 自动订阅状态变化,触发重新渲染 │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

6.2 异步数据流示例

// hooks/useCustomerList.ts
import { useState, useCallback } from 'react';
import { customerApi } from '@/api/customer';
import { useToast } from './useToast';

export function useCustomerList(initialParams?: CustomerListParams) {
const [data, setData] = useState<Customer[]>([]);
const [loading, setLoading] = useState(false);
const [params, setParams] = useState(initialParams || { pageNum: 1, pageSize: 20 });
const { showError } = useToast();

const fetch = useCallback(async (newParams?: Partial<CustomerListParams>) => {
const mergedParams = { ...params, ...newParams };
setLoading(true);

try {
const res = await customerApi.list(mergedParams);

if (mergedParams.pageNum === 1) {
setData(res.list);
} else {
setData(prev => [...prev, ...res.list]);
}

setParams(mergedParams);
return res;
} catch (error) {
showError('加载失败');
throw error;
} finally {
setLoading(false);
}
}, [params]);

const refresh = useCallback(() => fetch({ pageNum: 1 }), [fetch]);

const loadMore = useCallback(() => {
return fetch({ pageNum: params.pageNum + 1 });
}, [fetch, params.pageNum]);

return { data, loading, fetch, refresh, loadMore };
}

7. 核心模块设计

7.1 认证模块

┌─────────────────────────────────────────────────────────────┐
│ Authentication Flow │
│ │
│ ┌──────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ App │ → │ Splash │ → │ Check │ → │ Navigate │ │
│ │ 启动 │ │ Screen │ │ Token │ │ to... │ │
│ └──────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ │ │ │
│ Token 有效 Token 无效 │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ HomeTabs │ │ Login │ │
│ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Validate │ │
│ │ Email & │ │
│ │ Password │ │
│ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Login │ │
│ │ with │ │
│ │ VerifyCode│ │
│ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Save │ │
│ │ Token & │ │
│ │ UserInfo │ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────┘

7.2 CRM 模块

┌─────────────────────────────────────────────────────────────┐
│ CRM Module │
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ 客户管理 ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││
│ │ │ 客户列表 │ │ 客户详情 │ │ 客户编辑 │ ││
│ │ │ - 分页 │ │ - 基本信息│ │ - 表单 │ ││
│ │ │ - 搜索 │ │ - 品牌 │ │ - 验证 │ ││
│ │ │ - 筛选 │ │ - 项目 │ │ - 提交 │ ││
│ │ └──────────┘ │ - 合同 │ └──────────┘ ││
│ │ │ - 订单 │ ││
│ │ └──────────┘ ││
│ └─────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ 订单管理 ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││
│ │ │ 订单列表 │ │ 订单详情 │ │ 创建订单 │ ││
│ │ │ - 状态筛选│ │ - 产品 │ │ - 选客户 │ ││
│ │ │ - 时间筛选│ │ - 金额 │ │ - 选产品 │ ││
│ │ │ - 导出 │ │ - 审批流程│ │ - 提交 │ ││
│ │ └──────────┘ └──────────┘ └──────────┘ ││
│ └─────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ 审批管理 ││
│ │ ┌──────────┐ ┌──────────┐ ││
│ │ │ 待办审批 │ │ 审批详情 │ ││
│ │ │ - 我发起的│ │ - 同意 │ ││
│ │ │ - 待我审批│ │ - 拒绝 │ ││
│ │ │ - 我审批的│ │ - 转交 │ ││
│ │ └──────────┘ └──────────┘ ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

7.3 消息模块

┌─────────────────────────────────────────────────────────────┐
│ Message Module │
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ 企业消息 (企信) ││
│ │ ││
│ │ 消息类型: ││
│ │ - 待办事项通知 ││
│ │ - CRM 消息通知 ││
│ │ - 审批消息 ││
│ │ - 日程提醒 ││
│ │ - 系统通知 ││
│ └─────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ 实时通信 ││
│ │ ││
│ │ 技术方案: SSE (Server-Sent Events) ││
│ │ ││
│ │ ┌────────────────────────────────────────────────────┐││
│ │ │ Client Server │││
│ │ │ │ │ │││
│ │ │ │ ── POST /chat/msg ──────────> │ │││
│ │ │ │ │ │││
│ │ │ │ <── GET /chat/sse?msgId=xxx ─ │ │││
│ │ │ │ │ │││
│ │ │ │ <── [Event: message] ──────── │ │││
│ │ │ │ <── [Event: message] ──────── │ │││
│ │ │ │ <── [Event: done] ─────────── │ │││
│ │ │ │ │ │││
│ │ └────────────────────────────────────────────────────┘││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

8. 主题与样式系统

8.1 设计 Token

// theme/colors.ts
export const colors = {
// 品牌色 - 纷享销客橙色系
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',

// 功能色
success: '#1CC860',
warning: '#FE7E04',
error: '#FC5A5A',
info: '#2196F3',

// 中性色
text: '#192A3E',
textSecondary: '#666666',
textLight: '#999999',
textDisabled: '#D9D9D9',

// 背景色
background: '#F7F8FA',
backgroundSecondary: '#FFFFFF',
white: '#FFFFFF',

// 边框色
border: '#E5E5E5',
divider: '#E0E0E0',

// 渐变色
backgroundGradient: ['#d6edfb', '#f6f5fa', '#f8e6da'],
};

// theme/spacing.ts
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
};

// theme/typography.ts
export const typography = {
h1: { fontSize: 24, fontWeight: '700' as const },
h2: { fontSize: 20, fontWeight: '600' as const },
h3: { fontSize: 18, fontWeight: '600' as const },
body: { fontSize: 16, fontWeight: '400' as const },
bodySmall: { fontSize: 14, fontWeight: '400' as const },
caption: { fontSize: 12, fontWeight: '400' as const },
};

8.2 样式方案

项目采用 NativeWind (Tailwind CSS) + StyleSheet 混合方案:

// 推荐方式:Tailwind 类名
function Card() {
return (
<View className="bg-white rounded-lg p-4 shadow-md">
<Text className="text-lg font-semibold text-gray-800">
标题
</Text>
</View>
);
}

// 复杂样式:StyleSheet
const styles = StyleSheet.create({
container: {
...shadow.md,
backgroundColor: colors.white,
borderRadius: borderRadius.lg,
},
});

9. 错误处理策略

9.1 全局错误边界

// components/common/ErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { View, Text, Button } from 'react-native';

interface Props {
children: ReactNode;
fallback?: ReactNode;
}

interface State {
hasError: boolean;
error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };

static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// 上报错误到 Sentry
console.error('Error caught by boundary:', error, errorInfo);
}

handleRetry = () => {
this.setState({ hasError: false, error: null });
};

render() {
if (this.state.hasError) {
return this.props.fallback || (
<View className="flex-1 items-center justify-center p-4">
<Text className="text-lg font-semibold mb-2">出错了</Text>
<Text className="text-gray-500 mb-4">
{this.state.error?.message || '未知错误'}
</Text>
<Button title="重试" onPress={this.handleRetry} />
</View>
);
}

return this.props.children;
}
}

9.2 API 错误处理

// utils/errorHandler.ts
export enum ErrorCode {
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
SERVER_ERROR = 500,
NETWORK_ERROR = -1,
}

export function handleApiError(error: any): string {
const code = error.response?.status || error.code;

switch (code) {
case ErrorCode.UNAUTHORIZED:
// 跳转登录页
reset({ index: 0, routes: [{ name: 'Login' }] });
return '登录已过期,请重新登录';

case ErrorCode.FORBIDDEN:
return '您没有权限执行此操作';

case ErrorCode.NOT_FOUND:
return '请求的资源不存在';

case ErrorCode.SERVER_ERROR:
return '服务器错误,请稍后重试';

case ErrorCode.NETWORK_ERROR:
return '网络连接失败,请检查网络';

default:
return error.message || '请求失败,请稍后重试';
}
}

10. 性能优化策略

10.1 列表性能优化

// 使用 React.memo 避免不必要的重渲染
const CustomerItem = React.memo<{ customer: Customer }>(({ customer }) => {
return (
<View className="p-4 bg-white border-b border-gray-100">
<Text className="font-semibold">{customer.name}</Text>
</View>
);
});

// FlatList 优化配置
<FlatList
data={customers}
renderItem={({ item }) => <CustomerItem customer={item} />}
keyExtractor={(item) => item.id}
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={5}
initialNumToRender={10}
/>

10.2 图片优化

// 使用 FastImage 替代 Image
import FastImage from '@d11/react-native-fast-image';

<FastImage
source={{
uri: imageUrl,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
resizeMode={FastImage.resizeMode.cover}
style={{ width: 100, height: 100 }}
/>

10.3 状态更新优化

// 使用 useMemo 缓存计算结果
const filteredCustomers = useMemo(() => {
return customers.filter(c =>
c.name.includes(searchText) || c.phone?.includes(searchText)
);
}, [customers, searchText]);

// 使用 useCallback 缓存回调函数
const handlePress = useCallback(() => {
navigation.navigate('CustomerDetail', { customerId: customer.id });
}, [customer.id]);

11. 安全策略

11.1 敏感数据存储

// 使用 Keychain 存储敏感信息
import * as Keychain from 'react-native-keychain';

// 存储 Token
await Keychain.setGenericPassword('auth', token, {
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
});

// 读取 Token
const credentials = await Keychain.getGenericPassword();

// 删除 Token
await Keychain.resetGenericPassword();

11.2 输入验证

// 使用 Zod 进行输入验证
import { z } from 'zod';

const loginSchema = z.object({
email: z.string().email('请输入有效的邮箱地址'),
password: z.string().min(6, '密码至少6位'),
});

function validateLogin(data: unknown) {
const result = loginSchema.safeParse(data);
if (!result.success) {
throw new Error(result.error.errors[0].message);
}
return result.data;
}

12. 测试策略

12.1 单元测试

// __tests__/services/authService.test.ts
import { authService } from '@/services/authService';

describe('AuthService', () => {
it('should login successfully with valid credentials', async () => {
const result = await authService.login('test@example.com', 'password123');
expect(result.token).toBeDefined();
expect(result.user).toBeDefined();
});

it('should throw error with invalid credentials', async () => {
await expect(
authService.login('invalid@example.com', 'wrong')
).rejects.toThrow();
});
});

12.2 组件测试

// __tests__/components/CustomerCard.test.tsx
import { render, fireEvent } from '@testing-library/react-native';
import { CustomerCard } from '@/components/business';

const mockCustomer = {
id: '1',
name: '测试客户',
phone: '13800138000',
};

describe('CustomerCard', () => {
it('should render customer info correctly', () => {
const { getByText } = render(<CustomerCard customer={mockCustomer} />);
expect(getByText('测试客户')).toBeTruthy();
expect(getByText('13800138000')).toBeTruthy();
});

it('should call onPress when pressed', () => {
const onPress = jest.fn();
const { getByTestId } = render(
<CustomerCard customer={mockCustomer} onPress={onPress} />
);
fireEvent.press(getByTestId('customer-card'));
expect(onPress).toHaveBeenCalledWith(mockCustomer);
});
});

13. 部署与发布

13.1 多环境配置

# .env.dev
SALES_SERVER=https://dev-api.example.com
LOGIN_SERVER=https://dev-login.example.com
BUNDLE_ENV=dev

# .env.staging
SALES_SERVER=https://staging-api.example.com
LOGIN_SERVER=https://staging-login.example.com
BUNDLE_ENV=staging

# .env.prod
SALES_SERVER=https://api.example.com
LOGIN_SERVER=https://login.example.com
BUNDLE_ENV=prod

13.2 热更新 (CodePush)

# 发布到测试环境
appcenter codepush release-react -a {app-name} -d Staging \
--description "功能更新..." \
-t "1.0.0 - 1.0.5"

# 发布到生产环境
appcenter codepush release-react -a {app-name} -d Production \
--description "功能更新..." \
-t "1.0.0"

14. 后续优化建议

14.1 短期优化 (1-2周)

  1. API 模块拆分: 将 sales.js 拆分为独立的 API 模块 (customer.ts, order.ts, approval.ts)
  2. 类型完善: 为所有 API 请求/响应添加 TypeScript 类型定义
  3. 错误边界: 为关键页面添加 ErrorBoundary 组件
  4. 加载状态: 添加骨架屏 (Skeleton) 组件改善加载体验

14.2 中期优化 (1-2月)

  1. 离线支持: 添加数据缓存和离线访问能力
  2. 性能监控: 接入 Sentry 或 Firebase Performance
  3. 自动化测试: 完善单元测试和集成测试覆盖率
  4. 深度链接: 实现 Universal Links 和 Deep Links

14.3 长期规划

  1. 微前端架构: 考虑按业务模块拆分独立子应用
  2. 跨平台复用: 探索 React Native Web 支持
  3. CI/CD 完善: 自动化构建、测试、发布流程
  4. 设计系统: 建立完整的组件设计系统文档

附录

A. 命名规范

类型规范示例
文件夹kebab-casecustomer-list/
组件文件PascalCaseCustomerCard.tsx
工具文件camelCaseformatDate.ts
常量UPPER_SNAKE_CASEAPI_TIMEOUT
类型/接口PascalCaseCustomerInfo
变量camelCasecustomerList
Boolean 变量is/has/can 前缀isLoading, hasError

B. Git 提交规范

feat: 新功能
fix: 修复 bug
docs: 文档更新
style: 代码格式
refactor: 重构
perf: 性能优化
test: 测试
chore: 构建/工具

示例:
feat(customer): 添加客户搜索功能
fix(login): 修复登录失败无提示问题

C. 参考资源

微信公众号

微信公众号