基于业务场景的跨平台方案决策:
| 技术栈 | 开发效率 | 性能表现 | 合同场景适用性 | 典型案例 |
|---|---|---|---|---|
| React Native | 高(代码复用率80%) | 接近原生 | 复杂合同审批 | 阿里钉钉 |
| 微信小程序 | 极高(生态完善) | 优秀 | 轻量签署场景 | 腾讯电子签 |
| Flutter | 中(学习曲线陡) | 最佳 | 高交互合同编辑 | 字节飞书 |
| 原生开发 | 低(双端独立) | 极致 | 定制签署SDK | Adobe Sign |

核心层:React Native容器(iOS/Android)
业务层:小程序轻量模块(快速迭代)
能力层:原生插件(电子签名/生物认证)
桥接层:统一JS Bridge协议
合同审批功能的跨平台实现:
| 技术领域 | 核心库 | 合同场景应用 | 版本要求 |
|---|---|---|---|
| UI框架 | React Native 0.72+ | 合同列表/审批流 | 支持新架构 |
| 状态管理 | Zustand | 全局签署状态 | v4.0+ |
| 导航路由 | React Navigation 6.x | 多步骤签署流程 | 支持原生栈 |
| 原生能力 | TurboModules | 手写签名插件 | 新架构专用 |
合同审批组件:
// ContractApprovalScreen.js
import {useContractStore} from '../stores/contract';
const ContractApprovalScreen = ({route}) => {
const {contractId} = route.params;
const {approveContract, rejectContract} = useContractStore();
const [comment, setComment] = useState('');
const handleApprove = async () => {
try {
await approveContract(contractId, comment);
navigation.replace('ApprovalResult', {success: true});
} catch (error) {
Alert.alert('审批失败', error.message);
}
};
return (
<ScrollView contentContainerStyle={styles.container}>
<ContractViewer contractId={contractId} />
<TextInput
placeholder="请输入审批意见"
value={comment}
onChangeText={setComment}
style={styles.commentInput}
/>
<View style={styles.buttonGroup}>
<Button
title="拒绝"
onPress={rejectContract}
color="red"
/>
<Button
title="同意"
onPress={handleApprove}
color="green"
/>
</View>
</ScrollView>
);
};
// 手写签名原生模块
import {NativeModules} from 'react-native';
const {SignatureCapture} = NativeModules;
const SignaturePad = ({onSave}) => {
const showSignPad = async () => {
try {
const {path, width, height} = await SignatureCapture.open();
onSave({uri: `file://${path}`, width, height});
} catch (err) {
console.error('签名失败:', err);
}
};
return (
<TouchableOpacity onPress={showSignPad}>
<View style={styles.signPlaceholder}>
<Text>点击签名</Text>
</View>
</TouchableOpacity>
);
};TurboModule原生模块:
// SignatureCaptureModule.h (iOS)
#import <React/RCTBridgeModule.h>
@interface SignatureCaptureModule : NSObject <RCTTurboModule>
@end
// SignatureCaptureModule.mm
@implementation SignatureCaptureModule {
RCTPromiseResolveBlock _resolve;
}
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(open:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_resolve = resolve;
SignatureVC *vc = [[SignatureVC alloc] init];
vc.delegate = self;
[RCTPresentedViewController() presentViewController:vc animated:YES completion:nil];
});
}
- (void)signatureSaved:(NSString *)path width:(NSInteger)width height:(NSInteger)height {
if (_resolve) {
_resolve(@{@"path": path, @"width": @(width), @"height": @(height)});
_resolve = nil;
}
}
@end微信生态下的轻量签署方案:
| 小程序API | 合同功能 | 实现方案 | 注意事项 |
|---|---|---|---|
| 实时音视频 | 面签见证 | TRTC插件 | 企业主体认证 |
| 生物认证 | 签署人核身 | wx.startSoterAuth | Android兼容性 |
| 文件系统 | 合同缓存 | FileSystemManager | 100MB限制 |
| 云开发 | 签署状态同步 | 云数据库 | 配额管理 |
电子签名页面:
// pages/sign/index.wxml
<view class="container">
<canvas canvas-id="signPad"
disable-scroll
bindtouchstart="onTouchStart"
bindtouchmove="onTouchMove"
bindtouchend="onTouchEnd"></canvas>
<button type="primary" bindtap="confirmSign">确认签署</button>
</view>
// pages/sign/index.js
Page({
data: { strokes: [] },
onTouchStart(e) {
this.setData({
strokes: [...this.data.strokes, {
points: [{x: e.touches[0].x, y: e.touches[0].y}],
color: '#000',
width: 2
}]
});
this.drawSign();
},
onTouchMove(e) {
const lastStroke = this.data.strokes[this.data.strokes.length-1];
lastStroke.points.push({x: e.touches[0].x, y: e.touches[0].y});
this.drawSign();
},
drawSign() {
const ctx = wx.createCanvasContext('signPad');
ctx.setFillStyle('#fff');
ctx.fillRect(0, 0, 300, 150);
this.data.strokes.forEach(stroke => {
ctx.beginPath();
ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
stroke.points.forEach(p => ctx.lineTo(p.x, p.y));
ctx.setStrokeStyle(stroke.color);
ctx.setLineWidth(stroke.width);
ctx.stroke();
});
ctx.draw();
},
async confirmSign() {
try {
// 1. 保存签名图片
const {tempFilePath} = await new Promise(resolve => {
wx.canvasToTempFilePath({
canvasId: 'signPad',
success: resolve
});
});
// 2. 调用生物认证
const res = await wx.startSoterAuth({
requestAuthModes: ['fingerPrint'],
challenge: 'contract-' + this.data.contractId,
authContent: '请验证指纹确认签署'
});
// 3. 提交签署
wx.cloud.callFunction({
name: 'confirmSign',
data: {
contractId: this.data.contractId,
signImage: tempFilePath,
authResult: res.resultJSON
}
});
wx.navigateTo({url: '/pages/sign/success'});
} catch (err) {
wx.showToast({title: '签署失败', icon: 'error'});
}
}
})云函数签署验证:
// 云函数confirmSign
const cloud = require('wx-server-sdk')
cloud.init({env: cloud.DYNAMIC_CURRENT_ENV})
exports.main = async (event, context) => {
const {contractId, signImage, authResult} = event;
// 1. 验证生物认证结果
const authData = JSON.parse(authResult);
if (authData.rawAuthMsg !== md5('contract-' + contractId)) {
throw new Error('认证信息不匹配');
}
// 2. 上传签名图片到OSS
const fileRes = await cloud.uploadFile({
cloudPath: `signatures/${Date.now()}.png`,
fileContent: Buffer.from(signImage, 'base64')
});
// 3. 更新合同状态
const db = cloud.database();
await db.collection('contracts').doc(contractId).update({
data: {
status: 'signed',
signTime: db.serverDate(),
signImage: fileRes.fileID
}
});
// 4. 发送签署通知
await cloud.openapi.subscribeMessage.send({
touser: context.OPENID,
templateId: 'SIGN_RESULT_TEMPLATE',
data: {
thing1: {value: '合同签署完成'},
time2: {value: new Date().toLocaleString()}
}
});
return {success: true};
}弱网环境下的合同数据一致性保障:
| 同步模式 | 实现技术 | 合同场景用例 | 数据一致性 |
|---|---|---|---|
| 全量同步 | REST API | 首次加载 | 强一致 |
| 增量同步 | WebSocket | 实时状态更新 | 最终一致 |
| 冲突解决 | 版本向量 | 多端编辑 | 人工介入 |
| 本地优先 | SQLite+CRDT | 离线签署 | 自动合并 |
React Native离线存储:
// 使用WatermelonDB实现本地存储
import {Database} from '@nozbe/watermelondb';
import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite';
import {Contract, ContractComment} from './models';
const adapter = new SQLiteAdapter({
schema: [
Contract.schema,
ContractComment.schema
],
dbName: 'ContractDB',
});
export const database = new Database({
adapter,
modelClasses: [Contract, ContractComment],
});
// Contract模型定义
export default class Contract extends Model {
static table = 'contracts';
static associations = {
comments: {type: 'has_many', foreignKey: 'contract_id'},
};
@field('title') title;
@field('content') content;
@field('status') status;
@field('last_sync_at') lastSyncAt;
@children('comments') comments;
async syncWithServer() {
const lastSync = this.lastSyncAt || 0;
const response = await api.get(`/contracts/${this.id}/changes?since=${lastSync}`);
await this.batch(() => {
this.update(contract => {
for (const key in response.data) {
contract[key] = response.data[key];
}
contract.lastSyncAt = Date.now();
});
for (const comment of response.comments) {
this.collections.get('comments').prepareCreate(c => {
c.text = comment.text;
c.author = comment.author;
});
}
});
return this;
}
}离线操作队列:
// 操作队列服务
class OperationQueue {
constructor() {
this.queue = [];
this.isProcessing = false;
this.db = new Database();
}
async addOperation(type, payload) {
const op = {id: uuid(), type, payload, status: 'pending'};
await this.db.save('operations', op);
this.processQueue();
return op;
}
async processQueue() {
if (this.isProcessing) return;
this.isProcessing = true;
const operations = await this.db.getAll('operations', {status: 'pending'});
for (const op of operations) {
try {
await api.post('/operations', {
type: op.type,
payload: op.payload
});
await this.db.update('operations', op.id, {status: 'completed'});
} catch (err) {
if (err.isNetworkError) {
break; // 网络恢复后重试
}
await this.db.update('operations', op.id, {status: 'failed'});
}
}
this.isProcessing = false;
if (await this.db.count('operations', {status: 'pending'}) > 0) {
setTimeout(() => this.processQueue(), 5000);
}
}
}
// 离线签署示例
const queue = new OperationQueue();
await queue.addOperation('sign_contract', {
contractId: '123',
signature: 'base64-image',
timestamp: Date.now()
});开箱即用的移动端开发资源集合:
| 开发领域 | 开源方案 | 商业产品 | 合同场景适用 |
|---|---|---|---|
| 跨平台框架 | React Native | Flutter | 核心审批功能 |
| 小程序框架 | Taro | uni-app | 轻量签署场景 |
| 离线存储 | WatermelonDB | Realm | 合同本地缓存 |
关注「移动开发生态」公众号领取:
• 《React Native签名组件源码》
• 小程序电子签模板
• 离线同步方案白皮书
山西肇新科技
专注于提供合同管理领域,做最专业的合同管理解决方案。
请备注咨询合同系统