企业应用实战 #
一、项目概述 #
1.1 功能需求 #
| 模块 | 功能 |
|---|---|
| 认证 | 企业登录、SSO、生物识别 |
| 首页 | 工作台、待办事项、通知公告 |
| 审批 | 发起审批、审批列表、审批详情 |
| 通讯录 | 组织架构、员工搜索、部门管理 |
| 消息 | 企业通讯、群聊、文件传输 |
1.2 技术栈 #
- 前端框架: React + TypeScript
- 状态管理: Zustand
- UI组件: Ant Design Mobile
- 原生功能: Capacitor
- 离线支持: Service Worker + IndexedDB
二、项目初始化 #
2.1 创建项目 #
bash
# 创建项目
npm create vite@latest enterprise-app -- --template react-ts
cd enterprise-app
# 安装依赖
npm install
# 安装Capacitor
npm install @capacitor/core @capacitor/cli
npx cap init "企业应用" "com.company.enterprise"
# 安装插件
npm install @capacitor/preferences
npm install @capacitor/network
npm install @capacitor/status-bar
npm install @capacitor/splash-screen
npm install @capacitor/keyboard
npm install @capacitor/app
npm install @capacitor/browser
# 安装其他依赖
npm install zustand
npm install react-router-dom
npm install axios
npm install antd-mobile
npm install dayjs
npm install idb
2.2 项目结构 #
text
src/
├── api/
│ ├── index.ts
│ ├── auth.ts
│ ├── approval.ts
│ └── contacts.ts
├── components/
│ ├── Layout/
│ ├── ApprovalCard/
│ └── EmployeeCard/
├── hooks/
│ ├── useAuth.ts
│ ├── useOffline.ts
│ └── useSync.ts
├── pages/
│ ├── Login/
│ ├── Home/
│ ├── Approval/
│ ├── Contacts/
│ └── Messages/
├── services/
│ ├── auth.service.ts
│ ├── offline.service.ts
│ └── sync.service.ts
├── store/
│ ├── authStore.ts
│ ├── approvalStore.ts
│ └── offlineStore.ts
├── types/
│ └── index.ts
├── utils/
│ ├── crypto.ts
│ ├── storage.ts
│ └── db.ts
├── App.tsx
└── main.tsx
三、企业认证 #
3.1 认证服务 #
typescript
// src/services/auth.service.ts
import { Preferences } from '@capacitor/preferences';
import { BiometricAuth } from '@capacitor-community/biometric';
import { Capacitor } from '@capacitor/core';
import { api } from '../api';
import { Encryption } from '../utils/crypto';
interface LoginCredentials {
username: string;
password: string;
companyCode?: string;
}
interface AuthResult {
success: boolean;
user?: any;
token?: string;
error?: string;
}
class AuthService {
private tokenKey = 'auth_token';
private userKey = 'user_info';
private biometricKey = 'biometric_enabled';
async login(credentials: LoginCredentials): Promise<AuthResult> {
try {
const response = await api.post('/auth/login', credentials);
const { token, user } = response.data;
// 安全存储token
await this.secureStore(this.tokenKey, token);
await Preferences.set({
key: this.userKey,
value: JSON.stringify(user)
});
return { success: true, user, token };
} catch (error: any) {
return {
success: false,
error: error.response?.data?.message || '登录失败'
};
}
}
async logout(): Promise<void> {
try {
await api.post('/auth/logout');
} finally {
await Preferences.remove({ key: this.tokenKey });
await Preferences.remove({ key: this.userKey });
}
}
async getToken(): Promise<string | null> {
return this.secureRetrieve(this.tokenKey);
}
async getUser(): Promise<any | null> {
const { value } = await Preferences.get({ key: this.userKey });
return value ? JSON.parse(value) : null;
}
// 生物识别登录
async enableBiometric(): Promise<boolean> {
if (!Capacitor.isNativePlatform()) return false;
try {
const result = await BiometricAuth.checkBiometry();
if (!result.isAvailable) {
return false;
}
// 验证生物识别
const authResult = await BiometricAuth.authenticate({
reason: '启用生物识别登录',
title: '身份验证'
});
if (authResult.isAuthenticated) {
await Preferences.set({
key: this.biometricKey,
value: 'true'
});
return true;
}
return false;
} catch {
return false;
}
}
async loginWithBiometric(): Promise<AuthResult> {
try {
const { value: enabled } = await Preferences.get({
key: this.biometricKey
});
if (enabled !== 'true') {
return { success: false, error: '生物识别未启用' };
}
const authResult = await BiometricAuth.authenticate({
reason: '请验证身份以登录',
title: '生物识别登录'
});
if (authResult.isAuthenticated) {
const token = await this.getToken();
const user = await this.getUser();
if (token && user) {
return { success: true, user, token };
}
}
return { success: false, error: '验证失败' };
} catch (error: any) {
return { success: false, error: error.message };
}
}
private async secureStore(key: string, value: string): Promise<void> {
// 简化版:实际应使用加密存储
await Preferences.set({ key, value });
}
private async secureRetrieve(key: string): Promise<string | null> {
const { value } = await Preferences.get({ key });
return value;
}
}
export const authService = new AuthService();
3.2 登录页面 #
tsx
// src/pages/Login/index.tsx
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Button, Input, Form, Toast } from 'antd-mobile';
import { authService } from '../../services/auth.service';
import { useAuthStore } from '../../store/authStore';
import { Capacitor } from '@capacitor/core';
export default function Login() {
const [loading, setLoading] = useState(false);
const [biometricLoading, setBiometricLoading] = useState(false);
const navigate = useNavigate();
const { login } = useAuthStore();
async function handleSubmit(values: { username: string; password: string }) {
setLoading(true);
const result = await authService.login(values);
if (result.success) {
login(result.user, result.token!);
navigate('/');
} else {
Toast.show({ content: result.error });
}
setLoading(false);
}
async function handleBiometricLogin() {
setBiometricLoading(true);
const result = await authService.loginWithBiometric();
if (result.success) {
login(result.user!, result.token!);
navigate('/');
} else {
Toast.show({ content: result.error });
}
setBiometricLoading(false);
}
return (
<div className="login-page">
<div className="login-header">
<h1>企业办公</h1>
<p>欢迎登录</p>
</div>
<Form onFinish={handleSubmit}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input placeholder="用户名" />
</Form.Item>
<Form.Item name="password" rules={[{ required: true }]}>
<Input type="password" placeholder="密码" />
</Form.Item>
<Button
type="submit"
color="primary"
block
loading={loading}
>
登录
</Button>
</Form>
{Capacitor.isNativePlatform() && (
<Button
onClick={handleBiometricLogin}
block
loading={biometricLoading}
style={{ marginTop: 16 }}
>
生物识别登录
</Button>
)}
</div>
);
}
四、离线支持 #
4.1 IndexedDB封装 #
typescript
// src/utils/db.ts
import { openDB, DBSchema, IDBPDatabase } from 'idb';
interface EnterpriseDB extends DBSchema {
approvals: {
key: string;
value: {
id: string;
data: any;
syncStatus: 'pending' | 'synced' | 'failed';
updatedAt: number;
};
};
employees: {
key: string;
value: {
id: string;
data: any;
updatedAt: number;
};
};
offlineQueue: {
key: string;
value: {
id: string;
action: string;
data: any;
createdAt: number;
};
};
}
class DatabaseService {
private db: IDBPDatabase<EnterpriseDB> | null = null;
async init(): Promise<void> {
this.db = await openDB<EnterpriseDB>('enterprise-db', 1, {
upgrade(db) {
db.createObjectStore('approvals', { keyPath: 'id' });
db.createObjectStore('employees', { keyPath: 'id' });
db.createObjectStore('offlineQueue', { keyPath: 'id' });
}
});
}
// 审批数据
async saveApproval(id: string, data: any, syncStatus: 'pending' | 'synced' | 'failed' = 'pending') {
await this.db?.put('approvals', {
id,
data,
syncStatus,
updatedAt: Date.now()
});
}
async getApproval(id: string) {
return this.db?.get('approvals', id);
}
async getAllApprovals() {
return this.db?.getAll('approvals') || [];
}
// 员工数据
async saveEmployee(id: string, data: any) {
await this.db?.put('employees', {
id,
data,
updatedAt: Date.now()
});
}
async getEmployee(id: string) {
return this.db?.get('employees', id);
}
async getAllEmployees() {
return this.db?.getAll('employees') || [];
}
// 离线队列
async addToQueue(action: string, data: any) {
await this.db?.put('offlineQueue', {
id: `${Date.now()}-${Math.random()}`,
action,
data,
createdAt: Date.now()
});
}
async getQueue() {
return this.db?.getAll('offlineQueue') || [];
}
async removeFromQueue(id: string) {
await this.db?.delete('offlineQueue', id);
}
async clearQueue() {
const tx = this.db?.transaction('offlineQueue', 'readwrite');
await tx?.store.clear();
}
}
export const db = new DatabaseService();
4.2 离线同步服务 #
typescript
// src/services/sync.service.ts
import { Network } from '@capacitor/network';
import { db } from '../utils/db';
import { api } from '../api';
class SyncService {
private syncing = false;
async init(): Promise<void> {
await db.init();
// 监听网络状态
Network.addListener('networkStatusChange', (status) => {
if (status.connected) {
this.sync();
}
});
// 如果在线,立即同步
const status = await Network.getStatus();
if (status.connected) {
await this.sync();
}
}
async sync(): Promise<void> {
if (this.syncing) return;
this.syncing = true;
try {
// 同步离线队列
await this.syncQueue();
// 同步审批数据
await this.syncApprovals();
// 同步员工数据
await this.syncEmployees();
} catch (error) {
console.error('Sync error:', error);
} finally {
this.syncing = false;
}
}
private async syncQueue(): Promise<void> {
const queue = await db.getQueue();
for (const item of queue) {
try {
await this.processQueueItem(item);
await db.removeFromQueue(item.id);
} catch (error) {
console.error('Queue item sync failed:', error);
}
}
}
private async processQueueItem(item: any): Promise<void> {
switch (item.action) {
case 'submit_approval':
await api.post('/approvals', item.data);
break;
case 'approve':
await api.post(`/approvals/${item.data.id}/approve`, item.data);
break;
case 'reject':
await api.post(`/approvals/${item.data.id}/reject`, item.data);
break;
}
}
private async syncApprovals(): Promise<void> {
const response = await api.get('/approvals');
for (const approval of response.data) {
await db.saveApproval(approval.id, approval, 'synced');
}
}
private async syncEmployees(): Promise<void> {
const response = await api.get('/employees');
for (const employee of response.data) {
await db.saveEmployee(employee.id, employee);
}
}
async submitOffline(action: string, data: any): Promise<void> {
// 先保存到本地
await db.addToQueue(action, data);
// 如果在线,尝试同步
const status = await Network.getStatus();
if (status.connected) {
await this.sync();
}
}
}
export const syncService = new SyncService();
4.3 离线Hook #
typescript
// src/hooks/useOffline.ts
import { useState, useEffect } from 'react';
import { Network, ConnectionStatus } from '@capacitor/network';
export function useOffline() {
const [isOnline, setIsOnline] = useState(true);
const [connectionType, setConnectionType] = useState<string>('unknown');
useEffect(() => {
// 获取初始状态
Network.getStatus().then((status: ConnectionStatus) => {
setIsOnline(status.connected);
setConnectionType(status.connectionType);
});
// 监听变化
Network.addListener('networkStatusChange', (status: ConnectionStatus) => {
setIsOnline(status.connected);
setConnectionType(status.connectionType);
});
return () => {
Network.removeAllListeners();
};
}, []);
return {
isOnline,
isOffline: !isOnline,
connectionType
};
}
五、审批流程 #
5.1 审批Store #
typescript
// src/store/approvalStore.ts
import { create } from 'zustand';
import { db } from '../utils/db';
import { syncService } from '../services/sync.service';
import { useOffline } from '../hooks/useOffline';
interface Approval {
id: string;
type: string;
title: string;
applicant: { id: string; name: string };
status: 'pending' | 'approved' | 'rejected';
currentApprover?: { id: string; name: string };
createdAt: string;
content: any;
}
interface ApprovalStore {
approvals: Approval[];
loading: boolean;
loadApprovals: () => Promise<void>;
submitApproval: (data: any) => Promise<void>;
approve: (id: string, comment: string) => Promise<void>;
reject: (id: string, reason: string) => Promise<void>;
}
export const useApprovalStore = create<ApprovalStore>((set, get) => ({
approvals: [],
loading: false,
loadApprovals: async () => {
set({ loading: true });
try {
// 先从本地加载
const localApprovals = await db.getAllApprovals();
set({ approvals: localApprovals.map(a => a.data) });
// 如果在线,从服务器同步
const status = await Network.getStatus();
if (status.connected) {
// 同步后更新
}
} finally {
set({ loading: false });
}
},
submitApproval: async (data) => {
const id = `local-${Date.now()}`;
// 保存到本地
await db.saveApproval(id, {
...data,
status: 'pending',
createdAt: new Date().toISOString()
}, 'pending');
// 添加到离线队列
await syncService.submitOffline('submit_approval', data);
// 更新状态
set(state => ({
approvals: [...state.approvals, { id, ...data }]
}));
},
approve: async (id, comment) => {
await syncService.submitOffline('approve', { id, comment });
set(state => ({
approvals: state.approvals.map(a =>
a.id === id ? { ...a, status: 'approved' } : a
)
}));
},
reject: async (id, reason) => {
await syncService.submitOffline('reject', { id, reason });
set(state => ({
approvals: state.approvals.map(a =>
a.id === id ? { ...a, status: 'rejected' } : a
)
}));
}
}));
六、总结 #
6.1 企业级特性 #
| 特性 | 实现方式 |
|---|---|
| 安全认证 | 生物识别 + Token |
| 离线支持 | IndexedDB + 同步队列 |
| 数据安全 | 加密存储 |
| 网络监控 | Network插件 |
6.2 最佳实践 #
- 敏感数据加密存储
- 完善的离线支持
- 自动同步机制
- 企业级安全策略
恭喜你完成了Capacitor完全指南的学习!
最后更新:2026-03-28