123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- <template>
- <view class="auth-container">
- <!-- 身份证上传区域 -->
- <view class="upload-section">
- <text class="upload-title">身份证照片</text>
- <view class="idcard-wrapper">
- <!--<view class="upload-item" @click="uploadIDCard('front')">
- <image v-if="idFront" :src="idFront" class="preview-image" mode="aspectFill" />
- <text v-else class="placeholder">待上传</text>
- </view>
- <view class="upload-item" @click="uploadIDCard('back')">
- <image v-if="idBack" :src="idBack" class="preview-image" mode="aspectFill" />
- <text v-else class="placeholder">待上传</text>
- </view>-->
- <view class="upload-item" v-for="type in ['front', 'back']" :key="type" @click="uploadIDCard(type)">
- <image v-if="type === 'front' ? idFront : idBack"
- :src="type === 'front' ? idFront : idBack"
- class="preview-image" />
- <text v-if="!(type === 'front' ? idFront : idBack)"
- class="placeholder">点击上传{{ type === 'front' ? '正面' : '背面' }}</text>
- <!-- 上传状态层 -->
- <view v-if="uploadStatus[type].loading" class="upload-mask">
- <text>识别中...</text>
- </view>
- <view v-if="uploadStatus[type].result" class="upload-mask">
- <text v-if="uploadStatus[type].result === 'success'">识别成功</text>
- <text v-if="uploadStatus[type].result === 'fail'">{{uploadStatus[type].info}}</text>
- </view>
- <!-- 识别结果 -->
- <!--<view v-if="uploadStatus[type].result"
- class="result-tag"
- :class="uploadStatus[type].result">
- {{ uploadStatus[type].result === 'success' ? '✓' : '✕' }}
- </view>-->
- </view>
- </view>
- </view>
- <!--<!– 表单区域 –>
- <view class="form-section">
- <view class="form-item">
- <text class="label">姓名</text>
- <input class="input" v-model="formData.realName" placeholder="请输入姓名" />
- </view>
- <view class="form-item">
- <text class="label">身份证号</text>
- <input class="input" v-model="formData.idCard" placeholder="请输入身份证号" />
- </view>
- </view>-->
- <view class="form-section">
- <view class="form-item" v-for="(item, key) in filteredFields" :key="key">
- <text class="label">{{ item.label }}</text>
- <input
- class="input"
- v-model="item.value"
- :placeholder="'请输入'+item.label"
- />
- </view>
- </view>
- <!-- 操作按钮 -->
- <view class="action-buttons">
- <button class="btn secondary" @click="handleBack">返回</button>
- <button class="btn primary"
- :disabled="!canSubmit || loading"
- @click="handleSubmit">
- {{ loading ? '提交中...' : '去认证' }}
- </button>
- <!--<button class="btn primary" @click="handleSubmit">去认证</button>-->
- </view>
- </view>
- </template>
- <script>
- import { authApi } from '@/api/auth.js';
- import UUpload from "@/uview-ui/components/u-upload/u-upload.vue";
- export default {
- components: {UUpload},
- data() {
- return {
- isAuthed: false, // 是否已认证通过
- loading: false,
- accessToken:'',
- uploadStatus: {
- front: { loading: false, result: null,info:null },
- back: { loading: false, result: null,info:null }
- },
- idFront: '', // 身份证正面
- idBack: '', // 身份证反面
- idFrontFile: {}, // 身份证正面
- idBackFile: {}, // 身份证反面
- fieldLabels: {
- // orderid: "流水号",
- realname: "姓名",
- sex: "性别",
- nation: "民族",
- born: "生日",
- address: "籍贯",
- idcard: "证件号",
- begin: "生效日期",
- end: "截止日期",
- department: "发放机关"
- },
- formData: {
- // orderid: null,
- realname: null,
- sex: null,
- nation: null,
- born: null,
- address: null,
- idcard: null,
- begin: null,
- end: null,
- department: null,
- // realName: '梁妙杏',
- // idCard: '460034199204034721'
- },
- }
- },
- computed: {
- // 提交按钮是否可用
- canSubmit() {
- return (
- // 必须填写表单
- // this.formData.realName &&
- // this.formData.idCard &&
- // 必须上传成功或已有认证数据
- (this.idFront || this.isAuthed) &&
- (this.idBack || this.isAuthed)
- );
- },
- // 过滤处理后的字段数据
- filteredFields() {
- return Object.entries(this.formData)
- .filter(([key, value]) =>
- value !== null &&
- this.fieldLabels.hasOwnProperty(key)
- )
- .reduce((acc, [key, value]) => {
- acc[key] = {
- label: this.fieldLabels[key],
- value: value
- };
- return acc;
- }, {});
- }
- },
- onLoad(options) {
- this.initData(options);
- },
- methods: {
- initData(options) {
- try {
- if (options.payload) {
- const payload = JSON.parse(decodeURIComponent(options.payload));
- this.originalData = payload.threeAuthInfo || {};
- // 初始化表单数据
- this.formData = { ...this.formData, ...this.originalData };
- // 初始化身份证图片
- if (this.originalData.front) {
- this.idFront = this.originalData.front;
- this.idBack = this.originalData.back;
- }
- // 设置编辑模式
- this.isEditMode = !!this.originalData.idcard;
- }
- } catch (e) {
- console.error('参数解析失败:', e);
- }
- },
- // 上传身份证照片
- async uploadIDCard(type) {
- try {
- // 重置状态
- this.$set(this.uploadStatus[type], 'loading', false);
- this.$set(this.uploadStatus[type], 'result', null);
- const res = await uni.chooseImage({ count: 1 });
- const tempPath = res.tempFilePaths[0];
- // 显示临时图片预览
- if (type === 'front') this.idFront = tempPath;
- else this.idBack = tempPath;
- // 上传到服务器
- this.$set(this.uploadStatus[type], 'loading', true);
- this.$set(this.uploadStatus[type], 'result', null);
- const uploadRes = await this.uploadFile(type, tempPath);
- // 更新状态
- this.$set(this.uploadStatus[type], 'loading', false);
- this.$set(this.uploadStatus[type], 'result', 'success');
- this.$set(this.uploadStatus[type], 'info', '识别成功');
- // 保存服务器返回的URL
- if (type === 'front') {
- this.idFront = uploadRes.data['fileUrl'];
- }else {
- this.idBack = uploadRes.data['fileUrl'];
- }
- const filteredData = Object.fromEntries(
- Object.entries(uploadRes.data).filter(([_, v]) => v !== null)
- )
- this.formData = { ...this.formData, ...filteredData }
- } catch (error) {
- console.error('上传失败:', error);
- if (type === 'front') this.idFront = null;
- else this.idBack = null;
- this.$set(this.uploadStatus[type], 'loading', false);
- this.$set(this.uploadStatus[type], 'result', 'fail');
- this.$set(this.uploadStatus[type], 'info', error.msg);
- // uni.showToast({ title: '上传失败,请重试', icon: 'none' });
- }
- },
- // 文件上传方法
- async uploadFile(type, filePath) {
- return new Promise((resolve, reject) => {
- uni.uploadFile({
- url: authApi.ocrApi(),
- filePath,
- name: 'file',
- formData: { type },
- header: {
- Authorization: `Bearer ${this.accessToken}`,
- // 'Content-Type': 'multipart/form-data'//注意请求类型
- },
- success: (res) => {
- const data = JSON.parse(res.data);
- if (data.code === 0) {
- resolve(data);
- } else {
- reject(data);
- }
- },
- fail: reject
- });
- });
- },
- // 返回操作
- handleBack() {
- uni.navigateBack()
- },
- // 提交认证
- async handleSubmit() {
- if (!this.canSubmit) return;
- try {
- const postData = {};
- /*Object.keys(this.filteredFields).forEach((key) => {
- const item = this.filteredFields[key];
- if (item && item.value !== null) {
- postData[item.label] = item.value; // 使用label作为key
- }
- });*/
- Object.keys(this.filteredFields).forEach(originKey => {
- const fieldConfig = this.fieldLabels[originKey];
- const value = this.filteredFields[originKey]?.value;
- if (fieldConfig.required && !value) {
- throw new Error(`${fieldConfig.label}不能为空`);
- }
- postData[originKey] = value;
- });
- postData['front'] = this.idFront
- postData['back'] = this.idBack
- this.loading = true;
- const res = await authApi.threeElementApi(postData);
- if (res.code === 0) {
- //核验无误,需要输入验证码
- //显示弹窗验证码弹窗
- uni.showToast({ title: '认证成功' });
- this.isAuthed = true;
- // 更新本地数据
- // await this.loadAuthData();
- //活体
- const res2 = await authApi.personFaceApi({
- ...this.formData,
- front: this.idFront,
- back: this.idBack
- });
- console.log('res2res2res2',res2.data);
- if (res2.code === 0) {
- uni.navigateTo({
- url: `/pages/face-auth/face-auth?faceUrl=${encodeURIComponent(res2.data.faceUrl)}`
- });
- // 监听认证结果
- uni.$on('FACE_AUTH_SUCCESS', this.handleAuthSuccess);
- }
- }
- } finally {
- this.loading = false;
- }
- }
- },
- // 表单验证
- validateForm() {
- if (!this.idFront || !this.idBack) {
- uni.showToast({ title: '请上传身份证正反面照片', icon: 'none' })
- return false
- }
- if (!this.formData.realName.trim()) {
- uni.showToast({ title: '请输入姓名', icon: 'none' })
- return false
- }
- if (!/^\d{17}[\dXx]$/.test(this.formData.idCard)) {
- uni.showToast({ title: '请输入有效身份证号码', icon: 'none' })
- return false
- }
- return true
- },
- // 新增结果处理方法
- handleAuthSuccess(data) {
- console.log('检测活体检测 ',data)
- /*uni.showToast({ title: '活体认证成功' });
- // 处理后续业务逻辑...
- // 例如更新认证状态、跳转回个人中心等
- setTimeout(() => {
- uni.navigateBack({ delta: 2 }); // 返回两级页面
- }, 1500);*/
- },
- }
- </script>
- <style lang="scss" scoped>
- .auth-container {
- background: #fff;
- min-height: 100vh;
- padding: 40rpx;
- }
- .upload-section {
- margin-bottom: 40rpx;
- .upload-title {
- font-size: 32rpx;
- color: #333;
- display: block;
- margin-bottom: 30rpx;
- }
- .idcard-wrapper {
- display: flex;
- gap: 20rpx;
- }
- .upload-item {
- flex: 1;
- height: 240rpx;
- background: #f5f5f5;
- border-radius: 12rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- .preview-image {
- width: 100%;
- height: 100%;
- border-radius: 12rpx;
- }
- .placeholder {
- color: #999;
- font-size: 28rpx;
- }
- }
- }
- .form-item {
- display: flex;
- align-items: center;
- //margin-bottom: 40rpx;
- //padding: 20rpx 0;
- min-height: 96rpx;
- border-bottom: 1rpx solid #eee;
- .label {
- width: 160rpx;
- font-size: 28rpx;
- color: #666;
- flex-shrink: 0;
- }
- .input {
- flex: 1;
- height: 60rpx;
- font-size: 28rpx;
- color: #333;
- padding-left: 20rpx;
- &::placeholder {
- color: #999;
- }
- }
- }
- .action-buttons {
- position: fixed;
- bottom: 40rpx;
- left: 40rpx;
- right: 40rpx;
- display: flex;
- gap: 20rpx;
- .btn {
- flex: 1;
- height: 88rpx;
- line-height: 88rpx;
- border-radius: 44rpx;
- font-size: 32rpx;
- &.secondary {
- background: #f5f5f5;
- color: #666;
- }
- &.primary {
- background: #007aff;
- color: #fff;
- }
- }
- }
- .upload-item {
- position: relative;
- }
- .upload-mask {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0,0,0,0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- }
- .result-tag {
- position: absolute;
- right: 8px;
- bottom: 8px;
- width: 24px;
- height: 24px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- }
- .result-tag.success {
- background: #67C23A;
- color: white;
- }
- .result-tag.fail {
- background: #F56C6C;
- color: white;
- }
- </style>
|