Commit d1bfa69c authored by 罗剑's avatar 罗剑

fix: 提交OCR身份认证

parent d1f6b8d3
...@@ -11,6 +11,7 @@ const url = (REACT_APP_ENV || 'dev') !== 'dev' ? `${YOURSELFER_SERVER}/${BASEURL ...@@ -11,6 +11,7 @@ const url = (REACT_APP_ENV || 'dev') !== 'dev' ? `${YOURSELFER_SERVER}/${BASEURL
export const uploadFileUrl = `${BASEURL}/UploaderFiles`; // 上传文件链接 export const uploadFileUrl = `${BASEURL}/UploaderFiles`; // 上传文件链接
export const downloadFileUrl = `${BASEURL}/DownloadFiles`; // 下载/播放文件链接 export const downloadFileUrl = `${BASEURL}/DownloadFiles`; // 下载/播放文件链接
export const uploadCardFileUrl = `${BASEURL}/CardOCRUpLoad`; // 上传文件链接
/** @End */ /** @End */
......
...@@ -2516,6 +2516,146 @@ const advancedWidgets = [ ...@@ -2516,6 +2516,146 @@ const advancedWidgets = [
}, },
}, },
}, },
{
text: '身份证识别',
name: '身份证识别',
icon: <IconPack.FileUpload />,
schema: {
title: '身份证',
type: 'string',
widget: 'IDCard',
width: '100%',
preview: true,
},
setting: {
widget: {
title: '控件类型',
type: 'string',
widget: 'WidgetType',
displayType: 'row',
labelWidth: 80,
},
$id: {
title: '数据源',
type: 'string',
widget: 'FieldNames',
required: true,
},
title: {
title: '展示名称',
type: 'string',
required: true,
},
description: {
title: '字段说明',
type: 'string',
},
showField:{
title: '身份信息',
type: 'array',
widget:'CheckBoxGroup',
default: [],
},
// hiddenCondition: {
// title: '隐藏条件',
// type: 'string',
// description: '所有形态默认显示',
// widget: 'HiddenCondition'
// },
// options: {
// title: '',
// name: '选项',
// type: 'array',
// default: [{ value: '选项一' }],
// widget: 'SimpleList',
// dependencies: ['sourceType', 'color', 'isMultiple']
// },
// fileType: {
// title: '文件类型',
// type: 'string',
// widget: 'FileTypeSelect',
// enum: ['图片'],
// enumNames: ['图片'],
// default: '全部',
// },
durationTime: {
title: "{{formData.fileType+'最大时长'}}",
type: 'number',
description: '秒',
widget: 'slider',
default: 60,
min: 1,
max: 60,
hidden: "{{!['音频', '视频'].includes(formData.fileType)}}",
dependencies: ['fileType']
},
disabled: {
title: '只读',
type: 'boolean',
widget: 'checkbox',
default: false,
width: '50%',
},
required: {
title: '必填',
type: 'boolean',
widget: 'checkbox',
default: false,
width: '50%',
},
// preview: {
// title: '预览',
// type: 'boolean',
// widget: 'checkbox',
// default: true,
// width: '33%',
// },
// download: {
// title: '下载',
// type: 'boolean',
// widget: 'checkbox',
// default: true,
// width: '33%',
// },
photo: {
title: '允许从相册选取',
type: 'boolean',
widget: 'BooleanSwitch',
description: '仅支持移动端',
default: false,
hidden: "{{!['图片', '视频'].includes(formData.fileType)}}"
},
watermark: {
title: '水印',
type: 'boolean',
widget: 'BooleanSwitch',
description: '图片是否添加水印(仅支持移动端)',
default: false,
hidden: "{{!['图片'].includes(formData.fileType)}}"
},
groupStyle: {
title: '控件样式',
type: 'object',
properties: {}
},
width: {
title: '元素宽度',
type: 'string',
widget: 'percentSlider',
},
labelWidth: {
title: '标签宽度',
description: '默认值110',
default: 110,
type: 'number',
widget: 'slider',
max: 400,
props: {
hideNumber: true,
},
},
},
}
] ]
const settings = [ const settings = [
......
import React, { useEffect, useState } from 'react';
import styles from './index.less';
import { Upload, Button, message, Tabs, Form, Input } from 'antd';
import { UploadOutlined, FileOutlined } from '@ant-design/icons';
import FileViewer from 'react-file-viewer';
import { uploadFileUrl, downloadFileUrl, downloadFile , uploadCardFileUrl} from '../../../../apis/process';
import { filenameVerification } from '../../../../utils';
import Drag from '../../../components/Drag';
import * as XLSX from 'xlsx';
import { convertUrlToBase64 } from '../../../../utils/index';
const accepts = {
'图片': ['.bmp', '.gif', '.jpeg', '.tiff', '.png', '.svg', '.jpg'],
};
const getFileType = (fileName) => {
if (fileName) {
//图片
if (fileName.includes('jpg')) {
return 'jpg';
}
if (fileName.includes('png')) {
return 'png';
}
if (fileName.includes('svg')) {
return 'svg';
}
if (fileName.includes('jpeg')) {
return 'jpeg';
}
}
return null;
};
const IDCard = (props) => {
const site = window.globalConfig?.userInfo?.site || window.globalConfig?.userInfo?.LocalSite;
const { addons, value, schema, onChange } = props;
const { disabled, fileType, presetValue, placeholder, preview, download } = schema;
const [showList, setShowList] = useState([]);
const [visible, setVisible] = useState(false);
const [showFile, setShowFile] = useState({ fileType: '', filePath: '' });
const [workbook, setWorkbook] = useState({ SheetNames: [] });
const [cardSide, setCardSide] = useState("front");
const [cardInfo, setCardInfo] = useState({});
const showField = schema.showField || ['身份号码','姓名', '名族', '住址','出生','性别', '失效日期', '签发机关','签发日期'];
const showType = fileType === '图片' ? 'picture-card' : 'picture';
const option = {
name: 'file',
action: `${window.location.origin}${uploadCardFileUrl}?cardSide=${cardSide}&cardType=身份证`,
listType: showType,
// headers:{
// cardSide: "front"
// },
withCredentials: true,
showUploadList: {
showRemoveIcon: !disabled,
showDownloadIcon: download,
},
beforeUpload(file, fileList) {
/** @Tips: 解决提交文件中存在特殊字符的问题 */
let _continueUpload = true;
let _msg = {
type: 'success',
content: '上传成功!',
};
fileList.forEach(item => {
let _msgObject = filenameVerification(item);
if (_msgObject.type === 'error') {
_continueUpload = false;
_msg = {
type: 'error',
content: '上传失败!文件名不符合规则!',
};
}
});
_msg.type === 'error' ? message[_msg.type](_msg.content) : '';
return _continueUpload;
},
onChange: ({ file, fileList, event }) => {
// 检验名字,名字不通过不允许显示
if (filenameVerification(file).type === 'error') return false;
// 返回的链接在file.response内;不设置url,预览图表不可点击
if (file.status === 'done' && file.response.code === 0) {
// file.url = `${window.origin}${downloadFileUrl}?filePath=${file.response.data}${site ? `&_site=${site}` : ''}`
file.cardInfo = file.response.data;
const card = JSON.parse(file.response.data);
if(cardSide === "front"){
setCardInfo({...cardInfo, ...card});
setCardSide("back");
}else{
setCardInfo({...cardInfo, ...card});
setCardSide("front");
}
message.success('上传成功!');
} else if (file.status === 'done' && file.response.code !== 0) {
file.status = 'error';
message.error('上传失败!');
}
if (Array.isArray(fileList)) {
setShowList(fileList);
} else {
setShowList([]);
}
},
onPreview: async (file) => {
if (!preview) return message.info('该附件禁止预览');
let fileType = getFileType(file.name);
if (fileType) {
if (['xlsx', 'xls'].includes(fileType)) {
downloadFile(file.sourcePath)
.then(response => response.arrayBuffer())
.then(buffer => {
const data = new Uint8Array(buffer);
const workbook = XLSX.read(data, { type: 'array' });
setWorkbook(workbook);
workbook?.SheetNames?.forEach(sheetName => {
const worksheet = workbook.Sheets[sheetName];
// 将工作表转换为 JSON 对象
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: '' });
console.log(sheetName, jsonData);
});
});
}
setShowFile({ name: file.name, fileType: fileType, filePath: file.thumbUrl });
setVisible(true);
} else {
message.info('不支持该类型预览');
}
},
onRemove: async (file) => {
// let files = value?.split(",") || [];
// let list = files.filter(v => v !== file.name);
// setShowList(await valueToList(list.join(",")));
},
};
useEffect(() => {
onChange(JSON.stringify(cardInfo));
}, [cardInfo]);
const elemet = () => {
if (['jpg', 'png', 'svg', 'jpeg'].includes(showFile.fileType)) {
return (
<div style={{ width: '100%', height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<img style={{ maxWidth: '100%', maxHeight: '100%' }} src={showFile.filePath}></img>
</div>
);
} else if (['xlsx', 'xls'].includes(showFile.fileType)) {
return (
<div>
<Tabs>
{
workbook?.SheetNames?.map(sheetName => {
const worksheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: '' });
return (
<Tabs.TabPane tab={sheetName} key={sheetName}>
<div style={{ width: '100%', height: '550px', overflow: 'auto' }}>
<table
cellPadding={10}
style={{ width: '100%', height: '100%', display: 'block', tableLayout: 'fixed' }}
>
{
jsonData.map((row, rowIndex) => {
let maxLength = 0;
jsonData.forEach((v, i) => {
if (v.length > maxLength) {
maxLength = v.length;
}
});
let width = (100 / maxLength) + '%';
return (
<tbody>
<tr>
{
row.map((col, colIndex) => {
return (
<td
colSpan={row.length === 1 ? maxLength : 1}
style={{ border: '1px solid #aaa', width: width }}
>
{row[colIndex]}
</td>
);
})
}
</tr>
</tbody>
);
})
}
</table>
</div>
</Tabs.TabPane>
);
})
}
</Tabs>
</div>
);
}
return (
<FileViewer
className='fileViewer'
title='123'
fileType={showFile.fileType}
filePath={showFile.filePath}
/>
);
};
const iconRender = (file, listType) => {
if (listType !== 'text') {
if (fileType === '图片') {
return <img src={file.url}></img>;
}
let type = getFileType(file.name);
if (type) {
return <div className={styles.iconImg} type={type}></div>;
} else {
return <div className={styles.iconImg} type={'通用'}></div>;
}
}
return <FileOutlined />;
};
// const valueToList = async (presetValue) => {
// let fileList = [];
// if (presetValue) {
// let list = presetValue ? presetValue.split(',') : [];
// for (let i = 0; i < list.length; i++) {
// if (list[i]) { // @Tips: 直接过滤掉名字中有异常字符的文件
// let uid = i + '_' + Math.random();
// let _obj = {
// uid: uid,
// name: list[i].split('\\').reverse()[0],
// type: fileType === '图片' ? 'image/jpeg' : 'file',
// status: 'done',
// url: list[i]?.includes('http') ? list[i] : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`,
// sourcePath: list[i],
// // thumbUrl: list[i]?.includes('http') ? list[i] : (list[i].includes('svg') ? await convertUrlToBase64(`${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`) : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`),
// originFileObj: { "uid": uid },
// "response": { "code": 0, "msg": "Ok", "data": list[i], "stackTrace": null },
// "xhr": {},
// };
// if (fileType === '图片') {
// _obj.thumbUrl = list[i]?.includes('http') ? list[i] : (list[i].includes('svg') ? await convertUrlToBase64(`${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`) : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`);
// }
// fileList.push(_obj);
// }
// }
// }
// return fileList;
// };
const setList = async () => {
setCardInfo(JSON.parse(presetValue));
};
useEffect(() => {
if(presetValue && presetValue !== ""){
setList();
}
}, [presetValue]);
return (
<div className={fileType === '图片' ? styles.uploadBoxImg : styles.uploadBox}>
<Upload
multiple
accept={accepts[fileType]}
fileList={showList}
className="upload-list-inline"
iconRender={iconRender}
showUploadList={false}
{...option}
>
{
disabled ? null : <Button icon={<UploadOutlined />}>{placeholder || (cardSide === "front" ? '上传正面' : '上传背面')}</Button>
}
</Upload>
<Drag
width={'70%'}
title={showFile.name}
visible={visible}
onCancel={() => setVisible(false)}
bodyStyle={{ height: 650, overflowY: showFile.fileType.includes('xlsx') ? 'none' : "auto" }}
>
{elemet()}
</Drag>
<Form
layout="horizontal"
disabled={true}
style={{ maxWidth: '1200px' ,marginTop:'10px'}}
>
<div style={{display: 'flex',alignItems:'center',width:'100%',flexWrap:'wrap'}}>
{
showField.map((val,i)=>{
return (<Form.Item key={val} label={val} style={{width:'33%'}}><Input disabled style={{width:'200px'}} value={cardInfo[val]} /></Form.Item>);
})
}
</div>
</Form>
</div>
);
};
export default IDCard;
\ No newline at end of file
@import '~antd/es/style/themes/default.less';
@imgSrc: '../../../../assets/images/file';
.uploadBox,
.uploadBoxImg {
.iconImg {
width: 45px;
height: 45px;
margin: 0 auto;
&[type='通用'] {
background: url('@{imgSrc}/通用.png');
background-size: 100% 100%;
}
&[type='jpg'] {
background: url('@{imgSrc}/JPG.png');
background-size: 100% 100%;
}
&[type='png'] {
background: url('@{imgSrc}/PNG.png');
background-size: 100% 100%;
}
&[type="mp3"] {
background: url('@{imgSrc}/音乐.png');
background-size: 100% 100%;
}
&[type="mp4"] {
background: url('@{imgSrc}/视频.png');
background-size: 100% 100%;
}
&[type='docx'] {
background: url('@{imgSrc}/Word.png');
background-size: 100% 100%;
}
&[type='xlsx'],
&[type='xls'] {
background: url('@{imgSrc}/Excel.png');
background-size: 100% 100%;
}
&[type='PDF'] {
background: url('@{imgSrc}/PDF.png');
background-size: 100% 100%;
}
}
.@{ant-prefix}-form-item-label {
width: 100px;
text-align: left;
}
.ant-form-item{
margin-bottom: 12px;
}
}
.uploadBoxImg {
.@{ant-prefix}-upload-list-item-name {
display: none !important;
}
}
\ No newline at end of file
import RelationForm from './RelationForm' import RelationForm from './RelationForm'
import Signature from './Signature' import Signature from './Signature'
import IDCard from './IDCard'
const advanced = { const advanced = {
RelationForm, RelationForm,
Signature, Signature,
IDCard,
} }
export default advanced export default advanced
\ No newline at end of file
import React, { useEffect, useMemo, useState } from 'react';
import { Checkbox, Divider } from 'antd';
import styles from './index.less';
const plainOptions = ['身份号码','姓名', '名族', '住址','出生','性别', '失效日期', '签发机关','签发日期'];
const defaultCheckedList = ['姓名', '名族', '住址','身份号码','出生','性别', '失效日期', '签发机关','签发日期'];
const CheckboxGroup = Checkbox.Group;
const TableNames = (props) => {
const { value, schema ,onChange} = props;
const [checkedList, setCheckedList] = useState((value && value.length>0)
? value : defaultCheckedList);
const checkAll = plainOptions.length === checkedList.length;
const indeterminate = checkedList.length > 0 && checkedList.length < plainOptions.length;
const onChangeOne = (list) => {
setCheckedList(list);
onChange(list);
};
const onCheckAllChange = (e) => {
setCheckedList(e.target.checked ? plainOptions : []);
onChange(e.target.checked ? plainOptions : []);
};
return (
<div className={styles.CheckBoxGroup}>
<Checkbox indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}>
全选
</Checkbox>
<Divider />
<CheckboxGroup options={plainOptions} value={checkedList} onChange={onChangeOne} />
</div>
);
};
export default TableNames;
\ No newline at end of file
@import '~antd/es/style/themes/default.less';
.CheckBoxGroup {
width: 100%;
.ant-checkbox-group-item {
margin-right: 2px;
width: 90px;
}
}
\ No newline at end of file
import PercentSlider from './PercentSlider' import PercentSlider from './PercentSlider';
import ShowText from './ShowText' import ShowText from './ShowText';
import Formatting from './Formatting' import Formatting from './Formatting';
import DecimalDigits from './DecimalDigits' import DecimalDigits from './DecimalDigits';
import FileTypeSelect from './FileTypeSelect' import FileTypeSelect from './FileTypeSelect';
import ContentHTML from './ContentHTML' import ContentHTML from './ContentHTML';
import StyleHTML from './StyleHTML' import StyleHTML from './StyleHTML';
import RadioGroupW from './RadioGroupW' import RadioGroupW from './RadioGroupW';
import CheckBoxGroup from './CheckBoxGroup';
const groupStyle = { const groupStyle = {
PercentSlider, PercentSlider,
...@@ -16,6 +17,7 @@ const groupStyle = { ...@@ -16,6 +17,7 @@ const groupStyle = {
ContentHTML, ContentHTML,
StyleHTML, StyleHTML,
RadioGroupW, RadioGroupW,
} CheckBoxGroup,
};
export default groupStyle export default groupStyle;
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment