takephoto

256 阅读5分钟

react配置 速度上升到了函数可还是打开了函数的阿多久啊刷空间的可是大家阿斯利康多久阿的色即是空理解速度极乐世界的速度极乐世界绿色的就是理解速度就是快临近阿电话拉黑谁无 i 俄 u 为 iUI问我我也无语请问情怀手动

1.package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.15.1",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "antd-mobile": "^2.3.4",
    "lib-flexible": "^0.3.2",
    "node-sass": "^6.0.1",
    "postcss-px2rem": "^0.3.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^6.0.2",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.1.2"
  },
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "babel-plugin-import": "^1.13.3",
    "customize-cra": "^1.0.0",
    "react-app-rewire-postcss": "^3.0.2",
    "react-app-rewired": "^2.1.8"
  }
}

2.config-overrides.js

const { override, fixBabelImports, addPostcssPlugins } = require('customize-cra')

module.exports = override(
  addPostcssPlugins([
    require('postcss-px2rem')({ remUnit: 37.5 })
  ]),
  fixBabelImports('import', {
    libraryName: 'antd-mobile',
    style: 'css',
  })
)

3.src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import 'lib-flexible';
import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

4.src/index.css

body, h2, p, ul {
  margin: 0;
  padding: 0;
}

body {
  position: relative;
}

ul {
  list-style: none;
}

button {
  border: none;
  padding: 0;
  background-color: transparent;
}

5.src/App.js

import React from 'react'
import {
  HashRouter,
  Routes,
  Route,
  Navigate
} from 'react-router-dom'
import Home from './pages/home'
import IdCard from './pages/IdCard'
import Result from './pages/result'
import styles from './app.module.scss'

function App() {
  return (
    <div className={styles.app}>
      <HashRouter>
        <Routes>
          <Route path="/" element={<Home />}/>
          <Route path="IdCard" element={<IdCard />}/>
          <Route path="result" element={<Result />}/>
          <Route path="*" element={<Navigate replace to="/" />} />
        </Routes>
      </HashRouter>
    </div>
  );
}

export default App;

6.src/app.module.scss

.app {
  min-height: 100vh;
  background-color: #f9f9f9;
}

7.src/pages/home.jsx

import React from "react";
import { useNavigate, useSearchParams } from 'react-router-dom'
import { Button } from 'antd-mobile'
import styles from '../assets/styles/home.module.scss'

export default function Home() {
  // 获取网点编号、单位名称、单位地址
  const [searchParams] = useSearchParams()
  const No = searchParams.get('No')
  const companyName = searchParams.get('companyName')
  const address = searchParams.get('address')

  // 去采集身份证页
  const navigate = useNavigate()
  function toIDcard () {
    navigate('/IdCard', {
      state: {
        No,
        companyName,
        address
      }
    })
  }

  return (
    <div className={styles.home}>
      <h2>采集定位信息</h2>
      <ul>
        <li>
          <p className={styles.title}>单位:</p>
          <p className={styles.content}>{ companyName }</p>
        </li>
        <li>
          <p className={styles.title}>地址:</p>
          <p className={styles.content}>{ address }</p>
        </li>
      </ul>
      {/* 温馨提示图片 */}
      <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic124.nipic.com%2Ffile%2F20170317%2F13026516_162535430034_2.jpg&refer=http%3A%2F%2Fpic124.nipic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641022606&t=646784474b3763493ac080546a6ed611" alt="" srcSet="" />
      <Button
        onClick={toIDcard}
        type="primary"
        size="large"
        className={styles.btn}
      >
        开始
      </Button>
    </div>
  )
}

8.src/assets/styles/home.module.scss

.home {
  display: flex;
  flex-direction: column;
  padding: 20px;
  box-sizing: border-box;
  h2 {
    font: 700 20px/30px Ping Fang SC;
    color: #000;
  }
  ul {
    margin: 15px 0 20px;
    padding: 20px;
    border-radius: 12px;
    background-color: #fff;
    box-shadow: 0 15px 15px 0 rgba(0, 0, 0, 0.08);
    li {
      display: flex;
    }
    li + li {
      margin-top: 5px;
    }
  }
  img {
    border-radius: 12px;
    width: 100%;
    height: auto;
    margin-bottom: 25px;
  }
}

.title {
  font: 600 16px/24px Ping Fang SC;
  color: rgba(0, 0, 0, 0.6);
  min-width: 50px;
}

.content {
  width: calc(100% - 50px);
  word-wrap: break-word;
  font: 600 16px/24px Ping Fang SC;
  color: rgba(0, 0, 0, 1);
}

.btn {
  border-radius: 23.5px;
}

9.src/pages/IdCard.jsx

import React, { useState } from "react";
import { useLocation, useNavigate } from 'react-router-dom'
import { Button } from 'antd-mobile'
import styles from '../assets/styles/IdCard.module.scss'
import CheckBoxGroup from '../components/CheckBoxGroup'
import IdInput from '../components/IdInput'
import TakePhoto from '../components/takePhoto'
import request from "../api/http";

export default function IdCard() {
  const location = useLocation()
  console.log(location.state)

  // 勾选项
  const [checked, setChecked] = useState(1)
  // 身份证号
  const [ids, setIds] = useState('')
  // 手动输入身份证
  function handleChange(val) {
    console.log('val', val)
    setIds(val)
  }
  
  // 唤起相册拍照
  const [showPhoto, setShowPhoto] = useState(false)
  function takePhoto(status) {
    setShowPhoto(status)
  }

  const navigate = useNavigate()
  // 提交
  async function submit() {
    try {
      const res = await request({
        url: '/users/122',
        method: 'POST',
        data: {
          name: 'tester'
        }
      })
      console.log('res:', res)
    } catch (error) {
      console.log('error:', error)
    }
    // navigate('/result', {
    //   replace: true,
    //   state: {
    //     idNo: '23231'
    //   }
    // })
  }

  return (
    <div className={styles.idCard}>
      <header>
        <h2>输入身份证号码</h2>
        <span>(支持拍照自动识别)</span>
      </header>
      <IdInput value={ids} handleChange={handleChange} takePhoto={() => takePhoto(true)}/>
      <CheckBoxGroup checked={checked} check={setChecked} />
      <Button
        type='primary'
        onClick={submit} 
        className={styles.btn}
      >
        提交
      </Button>
      {
        showPhoto && <TakePhoto close={() => takePhoto(false)}/>
      }
    </div>
  )
}

10.src/assets/styles/IdCard.module.scss

.idCard {
  display: flex;
  flex-direction: column;
  padding: 20px;
  box-sizing: border-box;
  h2 {
    font: 700 20px/30px Ping Fang SC;
    color: #000;
  }
  header {
    display: flex;
    align-items: center;
    span {
      font: 700 16px/30px Ping Fang SC;
      color: rgba(0, 0, 0, 0.4);
      display: inline-block;
      margin-left: 4px;
    }
  }
}

.btn {
  border-radius: 23.5px;
  margin-top: 50px;
}

11.src/pages/result.jsx

import React from "react";
import { useLocation, useNavigate } from 'react-router-dom'
import { Button } from "antd-mobile";
import styles from '../assets/styles/result.module.scss'

export default function Result() {
  const location = useLocation()

  const navigate = useNavigate()

  function confirm () {
    navigate('/', {
      replace: true
    })
  }
  
  return (
    <div className={styles.result}>
      <h2>采集成功</h2>
      <p>您提交的身份证号码是</p>
      <p>{ location.state.idNo }</p>
      <Button type="primary" className={styles.btn} onClick={confirm}>确认</Button>
    </div>
  )
}

12.src/assets/styles/result.module.scss

.result {
  display: flex;
  flex-direction: column;
  padding: 20px;
  box-sizing: border-box;
  h2 {
    font: 700 20px/30px Ping Fang SC;
    color: #000;
    text-align: center;
    margin-bottom: 30px;
  }
  p {
    font: 700 18px/24px Ping Fang SC;
    text-align: center;
    color: rgba(#000000, 0.4)
  }
}

.btn {
  margin-top: 50px;
  border-radius: 23.5px;
}

13.src/components/takePhoto/index.jsx

import React, { useEffect } from "react";
import reactDom from "react-dom";
import { Button } from "antd-mobile";
import styles from './index.module.scss'

// 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。

function Camera(props) {

  useEffect(() => {
    // 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
    if (navigator.mediaDevices === undefined) {
      navigator.mediaDevices = {};
    }
    // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia 
    // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。
    if (navigator.mediaDevices.getUserMedia === undefined) {
      navigator.mediaDevices.getUserMedia = function (constraints) {
        // 首先,如果有getUserMedia的话,就获得它
        var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

        // 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口
        if (!getUserMedia) {
          return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
        }

        // 否则,为老的navigator.getUserMedia方法包裹一个Promise
        return new Promise(function (resolve, reject) {
          getUserMedia.call(navigator, constraints, resolve, reject);
        });
      }
    }

    const constraints = { audio: false, video: true };

    navigator.mediaDevices.getUserMedia(constraints)
      .then(function (stream) {
        var video = document.querySelector('video');
        // 旧的浏览器可能没有srcObject
        if ("srcObject" in video) {
          video.srcObject = stream;
        } else {
          // 防止在新的浏览器里使用它,应为它已经不再支持了
          video.src = window.URL.createObjectURL(stream);
        }
        video.onloadedmetadata = function (e) {
          video.play();
        };
      })
      .catch(function (err) { alert(err.name + ": " + err.message); });
  }, [])

  function takePicture () {
    var canvas = document.querySelector('#canvas');
    canvas.getContext('2d');
    var video = document.querySelector('video');
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    canvas.getContext('2d').drawImage(video,0,0);
    canvas.toBlob(function(blob){
      console.log(blob)
    });
  }

  return (
    <div className={styles.camera}>
      <div className={styles.cover}>
        <div className={styles.line}></div>
        <p>请将扫描线对准数字</p>
      </div>
      <video></video>
      <canvas id="canvas" />
      <Button onClick={takePicture} type="primary">拍照</Button>
    </div>
  )
}

export default function TakePhoto(props) {
  return reactDom.createPortal(<Camera {...props} />, document.body)
}

14.src/components/takePhoto/index.module.scss

.camera {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 999;
  overflow: hidden;
  background-color: #fff;
  video {
    width: 100vw;
    height: 500px;
    object-fit: cover;
  }
  canvas {
    position: absolute;
    z-index: -1;
  }
}

.cover {
  position: fixed;
  left: 20px;
  top: 400px;
  z-index: 10;
  width: calc(100vw - 40px);
  height: 50px;
  border: 2px solid #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  .line {
    height: 2px;
    background-color: aquamarine;
    width: 100%;
  }
  p {
    position: absolute;
    font: 600 14px/14px Ping Fang SC;
    color: blue;
    right: 2px;
    bottom: 2px;
  }
}

15.src/components/IdInput/index.jsx

import React from "react";
import cameraIcon from '../../assets/images/camera.png'
import styles from './index.module.scss'

export default function IdInput(props) {

  function change(e) {
    props.handleChange(e.target.value)
  }

  return (
    <div className={styles.IdInput}>
      <input value={props.value} type="tel" onChange={change} />
      <img onClick={props.takePhoto} src={cameraIcon} alt="" srcSet="" />
    </div>
  )
}

16.src/components/IdInput/index.module.scss

.IdInput {
  display: flex;
  align-items: center;
  margin-top: 20px;
  border: 1px solid rgba(0, 0, 0, 0.6);
  border-radius: 4px;
  input {
    border: none;
    padding: 0;
    width: 100%;
    padding: 0 8px;
    box-sizing: border-box;
    font: 600 20px/30px Ping Fang SC;
    color: #000;
  }
  img {
    width: 40px;
    min-width: 40px;
    height: auto;
  }
}

17.src/components/CheckBoxGroup/index.jsx

import React from "react";
import { Checkbox } from 'antd-mobile'
import styles from './index.module.scss'

const selectList = [
  {
    id: 1,
    label: '拜访'
  },
  {
    id: 2,
    label: '其他'
  }
]

// 单选框
export default function CheckBoxGroup(props) {

  function handleCheck(id) {
    props.check(id)
  }

  return (
    <div className={styles.checkBoxGroup}>
      <h2>业务办理</h2>
      <div className={styles.list}>
        {
          selectList.map(item => 
            <Checkbox
              onChange={() => handleCheck(item.id) }
              checked={props.checked === item.id }
              key={item.id}
              >
                <span className={styles.label}>{ item.label }</span>
            </Checkbox>
          )
        }
      </div>
    </div>
  )
}

18.src/components/CheckBoxGroup/index.module.scss

.checkBoxGroup {
  margin-top: 50px;
}

.list {
  margin-top: 20px;
  display: flex;
  align-items: center;
}

.label {
  font: 700 16px/20px Ping Fang SC;
  color: #000;
  margin-left: 4px;
}

:global {
  .am-checkbox-wrapper {
    display: flex;
    align-items: center;
  }
  .am-checkbox-wrapper + .am-checkbox-wrapper {
    margin-left: 20px;
  }
}

19.src/api/http.js

const base_url = 'https://api.github.com'

/**
 * 
 * @param {*} options 
 */
export default async function request(options = { url: '', method: 'GET', data: {} }) {
  const {
    url = '',
    method = 'GET',
    data = {}
  } = options
  try {
    const response = await window.fetch(`${base_url}${url}`, {
      method,
      headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: JSON.stringify(data)
    })
    console.log('response', response)
    if (response.ok) {
      const data = await response.json()
      console.log(data)
      return Promise.resolve(data)
    }
    return Promise.reject(response.status)
  } catch (error) {
    return Promise.reject(error)
  }
}