基于业务场景的跨平台方案决策:
技术栈 | 开发效率 | 性能表现 | 合同场景适用性 | 典型案例 |
---|---|---|---|---|
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签名组件源码》
• 小程序电子签模板
• 离线同步方案白皮书
山西肇新科技
专注于提供合同管理领域,做最专业的合同管理解决方案。
请备注咨询合同系统