React Native 开发APP

萧嘉茂
2023-12-01

记录一下项目中主要用到的技术点吧~

一.电子签名的显示,文件存储到手机,上传

电子签名用到的组件:react-native-signature-capture

写文件react-native-fs

上传用到的组件react-native-fetch-blob


import React, { Component, Fragment } from "react";
import { StyleSheet, View, TextInput, Text,TouchableOpacity, Image, FlatList, Dimensions,
} from "react-native";
import { common_url, fetchRequest } from "../../utils/request";
import Dialog, { DialogButton, DialogContent, DialogFooter, DialogTitle } from "react-native-popup-dialog";
import SignatureCapture from "react-native-signature-capture";
import RNFetchBlob from "react-native-fetch-blob";
var RNFS = require('react-native-fs');
export default class SignConstract extends Component {
  constructor(props) {
    super(props);
    this.state = {
      visible:false,
      contract:"",
      signImg:"",
    };
  }

 
  //签名
  _onSaveEvent(result) {
    if(result.encoded!=undefined){
      this.setState({
        signImg:result.encoded,  //result.encoded base64编码,用于签名图片的显示
      })
    }

     
    var timestamp = (new Date()).getTime();
    const path = RNFS.PicturesDirectoryPath + '/'+timestamp+'signature.png';
      // write the file
      RNFS.writeFile(path, result.encoded, 'base64')
        .then((success) => {
          console.log('存储在手机的文件路径path', path);
        })
        .catch((err) => {
          console.log(err.message);
        });


    //转化后的本地路径
    this.upload(result.pathName)
  }
 //上传文件
  upload(pathName){
    DeviceStorage.get("ownerId").then((ownerId) => {
      var declareId ="1";
      let body = [{
        name: "declareId", data:declareId
      },
        {
          name: "sign",
          filename: "测试",
          type: "image/png",
          data: RNFetchBlob.wrap(pathName)
        }];
  
      RNFetchBlob.fetch("POST", common_url+"hserp_owners/participateSing", {
        // 上传图片要设置Header
        "Content-Type":"multipart/form-data",
      }, body)
        .uploadProgress((written, total) => {
          // 本地查找进度
        })
        .progress((received, total) => {
          let perent = received / total;
          console.log("上传进度打印", perent);
        })
        .then((response) => response.json())
        .then((response) => {
          console.log("上传信息返回", response);
        })
        .catch((error) => {
          // 错误信息
          console.log(error);
        });


    });
  }
  //保存签名
  saveSign() {
    this.refs["sign"].saveImage();
  }
  //清除签名
  resetSign() {
    this.refs["sign"].resetImage();
  }

  render() {
    return (
      <View style={styles.container}>
          <View style={{ flex: 1 }}>
            <View style={styles.content}>
              <View style={styles.rowBorder}>
              <TouchableOpacity style={styles.rowContent} onPress={() => {
                this.setState({
                  visible: true
                });
              }}>
                <Text style={{color:theme.color}}>签名:</Text>
                <Image source={{ uri: "data:image/jpg;base64," + this.state.signImg }} style={styles.img}/>
              </TouchableOpacity>
            </View>

          </View>
          {/*确认*/}
          <Dialog
            width={0.9}
            height={0.7}
            visible={this.state.visible}
            footer={
              <DialogFooter>
                <DialogButton
                  text="取消"
                  onPress={() => {
                    this.setState({
                      visible: false
                    });
                  }}
                />
                <DialogButton
                  text="确定"
                  onPress={() => {
                    this.setState({
                      visible: false
                    }, () => {
                      this.saveSign();
                    });
                  }}
                />
              </DialogFooter>
            }
            dialogTitle={<DialogTitle title="签名确认"/>}>
            <DialogContent style={{ flex: 1 }}>
              <View style={{ flex: 1 }}>
                <SignatureCapture
                  ref="sign"
                  style={{ flex: 1 }}
                  minStrokeWidth={5}
                  maxStrokeWidth={5}
                  showNativeButtons={false}
                  saveImageFileInExtStorage={true}  //是否存储在手机
                  onSaveEvent={this._onSaveEvent.bind(this)}
                />
              </View>
            </DialogContent>
          </Dialog>
        <View style={styles.btn}>
          <Text style={styles.white}
                onPress={() => {
                  if(!this.state.signImg){
                    this.refs.toast.show("请签名", 4000);
                    return false

                  }else{
                    Actions.Renovation({sign:this.state.path})
                  }

                }}>下一步</Text>
        </View>
      </View>

    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: theme.gray
  },
});

 

二. 图片压缩 

用到的组件:react-native-image-resizer,上传前自己用promise做了个处理压缩多图

import React, { Component } from "react";
import {StyleSheet, View, ScrollView, TextInput, Text, TouchableOpacity, Image, FlatList, Dimensions, DeviceEventEmitter, ImageBackground} from "react-native";
import { common_url, fetchRequest } from "../../utils/request";
import RNFetchBlob from "react-native-fetch-blob";
import ImageResizer from 'react-native-image-resizer';
export default class RepaireApply extends Component {
  constructor(props) {
    super(props);
    this.state = {
      imgArr: [],
      typeArr: [],
      date: "",
      total: "",
      imgId: ""
    };
  }

  componentWillMount() {
    this.getList();
    this.deEmitter = DeviceEventEmitter.addListener("left", (a) => {
      this.setState({
        imgArr: a
      });
    });
  }

  componentWillUnmount() {
    this.deEmitter.remove();
  }

  async save() {
      DeviceStorage.get("ownerId").then(async(ownerId) => {
        let arr = [];
        this.state.imgArr.map((item, index) => {
          arr.push(ImageResizer.createResizedImage(item, 100, 100, "PNG", 100 ))
        })

        let arr2 = [];
        let result=await Promise.all(arr)
        result.map((item, index) => {
          var obj = {
            name: "picture" + index,
            filename: "picture" + item.name,
            type: "image/png",
            data: RNFetchBlob.wrap(item.uri)
          };
          arr2.push(obj);
        });

        arr2.push({
          name: "request", data: this.state.requests
        }, {
          name: "size", data: this.state.imgArr.length + ""
        });
        RNFetchBlob.fetch("POST", common_url + "hserp_owners/repairMaintenance", {
          "Content-Type": "multipart/form-data"
        }, arr2)
          .uploadProgress((written, total) => {
         
          })
          .progress((received, total) => {
           
          })
          .then((response) => response.json())
          .then((response) => {
            
          })
          .catch((error) => {
            // 错误信息
            console.log(error);
          });
      });
  }

 

三. 手势系统运用:动态改变scrollview 的滑动方向

import React, { Component } from "react";
import { StyleSheet,  View,Text, TouchableOpacity,ScrollView, PanResponder} from "react-native";
import { fetchRequest } from "../../utils/request";
var Dimensions = require("Dimensions");
export default class ParkSpace extends Component {
  constructor(props) {
    super(props);
    this.state = {
      horizontal:false
    };
  }

  componentWillMount() {
    
    this._panResponder = PanResponder.create({

      // 要求成为响应者
      // nativeEvent
      // gestureState  dx - 从触摸操作开始时的累计横向路程    dy - 从触摸操作开始时的累计纵向路程


      //表示是否成为滑动事件的劫持者,如果返回是,则不会把滑动事件传递给它的子元素。
      onStartShouldSetPanResponder: (evt, gestureState) => false,

      //表示是否成为事件的劫持者,如果返回是,则不会把事件传递给它的子元素
      onStartShouldSetPanResponderCapture: (evt, gestureState) => false,


      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onStartShouldSetResponderCapture: (evt)=> true,
      VonMoveShouldSetResponderCapture: (evt)=> true,

      //开始手势操作
      onPanResponderGrant: (evt, gestureState) => {
        // this._highlight();
        this.setState({
          horizontal:true
        })
      },


      //触摸点移动
      onPanResponderMove: (evt, gestureState) => {
        this.state.ScreenWidth,gestureState.moveX,)
        console.log(`gestureState.moveX  : ${gestureState.moveX }   gestureState.moveY  : ${gestureState.moveY }`);
        if(gestureState.moveX<this.state.ScreenWidth){
         //动态改变
          this.setState({
            horizontal:true
          })
        }else{
          this.setState({
            horizontal:false
          })
        }
      },
      //用户放开了所有的触摸点,且此时视图已经成为了响应者
      onPanResponderRelease: (evt, gestureState) => {
      },
      //另一个组件已经成为了新的响应者,所以当前手势将被取消
      onPanResponderTerminate: (evt, gestureState) => {
      },
    });
  }

  show(typeId,carType, price,number) {
    Actions.CartFee({"typeId":typeId,"carType": carType, "carPrice": price,"number":number,area:this.state.area });
  }

  render() {
    return (
      <View style={{display:"flex",height:"100%"}}>

        <ScrollView style={{flex:1}}
                    horizontal={this.state.horizontal} //水平
                    showsHorizontalScrollIndicator={true}
                    showsVerticalScrollIndicator={true}
                    removeClippedSubviews={false}
                    overScrollMode="always">

          <View  style={{width:this.state.parkWidth,height:this.state.parkHeight,backgroundColor: "#00ff00"}}
                 {...this._panResponder.panHandlers}>
            {this.state.cars.map((item) => {
              return (
                <TouchableOpacity style={{ position: "absolute", left: (item.abscissa_y - 1) * this.state.cellWidth, top: (item.abscissa_x - 1) * this.state.cellHeight }}
                                  key={item.id}
                                  onPress={() => {
                                    if (item.state != "3") {
                                      this.refs.toast.show("该车位已租售,请重新选择", 4000
                                      return false;
                                    } else {
                                      this.show(item.type_id[0],item.type_id[1], item.price,item.parking_number);
                                    }
                                  }}
                >
                  <View style={{ width:60,
                    height:30,
                    backgroundColor: "#848484",
                    borderColor: "#fff",
                    borderBottomWidth:2,
                    borderTopWidth:2,
                    borderLeftWidth:2,
                    transform: [{ rotate: item.angle+"deg"}]
                  }}>
                    <Text style={styles.zhu}>▎</Text>
                    <Text style={styles.zhu}>▎</Text>
                    <Text style={{position:"absolute", top:"30%", left:"30%", color:"white", fontSize:8,transform: [{ rotate: item.angle+"deg"}]}}>{item.parking_number}</Text>
                    <Text style={styles.tang}>▲</Text>

                  </View>
                </TouchableOpacity>
              );
            })}
          </View>
        </ScrollView>
        <Toast
          ref="toast"
          style={{ backgroundColor:theme.toastColor}}
          position='center'
          fadeInDuration={theme.fadeInDuration}
          fadeOutDuration={theme.fadeOutDuration}
          opacity={0.7}
          textStyle={{ color: "white" }}
        />
      </View>
    );
  }
}
const styles = StyleSheet.create({
});

 

 类似资料: