-
16pages.json
-
515pages/register/register-form.vue
-
470pages/register/register.vue
-
6pages/user/user.vue
-
BINstatic/register/1.jpg
-
BINstatic/register/2.jpg
-
BINstatic/register/3.jpg
-
BINstatic/register/4.jpg
-
BINstatic/register/5.jpg
-
BINstatic/register/6.jpg
-
BINstatic/register/7.jpg
-
0subpkg/pages/seat-record/seat-record.vue
@ -0,0 +1,515 @@ |
|||
<template> |
|||
<view class="page-container"> |
|||
<!-- 顶部背景 + 标题 --> |
|||
<view class="top-bar"> |
|||
<image class="top-bar-bg" src="@/static/images/mingqi-beij@2x.png" mode="aspectFill"></image> |
|||
<view class="library-info"> |
|||
<image class="avatar" src="@/static/images/logo.jpg" mode="aspectFill"></image> |
|||
<view class="library-name">葛店经济技术开发区图书馆</view> |
|||
<view class="sub-title">读者证办理</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 表单卡片 --> |
|||
<view class="form-card"> |
|||
<!-- 姓名 --> |
|||
<view class="form-item"> |
|||
<uni-icons class="form-icon" custom-prefix="iconfont" type="icon-xingming" size="22"></uni-icons> |
|||
<input |
|||
class="input" |
|||
placeholder="请输入真实姓名" |
|||
v-model="formData.name" |
|||
maxlength="20" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 身份证号 --> |
|||
<view class="form-item"> |
|||
<uni-icons class="form-icon" custom-prefix="iconfont" type="icon-duzhezheng" size="24"></uni-icons> |
|||
<input |
|||
class="input" |
|||
placeholder="请输入18位身份证号" |
|||
v-model="formData.idCard" |
|||
maxlength="18" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 手机号 --> |
|||
<view class="form-item"> |
|||
<uni-icons class="form-icon" type="phone" size="24"></uni-icons> |
|||
<input |
|||
class="input" |
|||
placeholder="请输入手机号" |
|||
v-model="formData.phone" |
|||
type="number" |
|||
maxlength="11" |
|||
/> |
|||
<button class="get-phone-btn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"> |
|||
一键获取 |
|||
</button> |
|||
</view> |
|||
|
|||
<!-- 密码 --> |
|||
<view class="form-item"> |
|||
<uni-icons class="form-icon" type="locked" size="24"></uni-icons> |
|||
<input |
|||
class="input" |
|||
placeholder="请设置6-20位密码" |
|||
:password="!showPwd" |
|||
v-model="formData.password" |
|||
maxlength="20" |
|||
/> |
|||
<uni-icons |
|||
class="pwd-icon" |
|||
:type="showPwd ? 'eye' : 'eye-slash'" |
|||
size="20" |
|||
@click="togglePwd" |
|||
></uni-icons> |
|||
</view> |
|||
|
|||
<!-- 确认密码 --> |
|||
<view class="form-item"> |
|||
<uni-icons class="form-icon" type="locked" size="24"></uni-icons> |
|||
<input |
|||
class="input" |
|||
placeholder="请再次确认密码" |
|||
:password="!showConfirmPwd" |
|||
v-model="formData.confirmPassword" |
|||
maxlength="20" |
|||
/> |
|||
<uni-icons |
|||
class="pwd-icon" |
|||
:type="showConfirmPwd ? 'eye' : 'eye-slash'" |
|||
size="20" |
|||
@click="toggleConfirmPwd" |
|||
></uni-icons> |
|||
</view> |
|||
|
|||
<!-- 提交按钮 --> |
|||
<button class="submit-btn" type="primary" @click="submit" :loading="loading"> |
|||
{{ loading ? '提交中...' : '确认办理' }} |
|||
</button> |
|||
|
|||
<!-- 同意协议 --> |
|||
<view class="agreement-item"> |
|||
<view class="radio-box" :class="{ checked: agreed }" @click="agreed = !agreed"> |
|||
<view class="radio-inner" v-if="agreed"></view> |
|||
</view> |
|||
<text class="agreement-text"> |
|||
我已阅读并同意遵守 |
|||
<text class="agreement-link" @click="openAgreement">《爱图智服服务协议》</text> |
|||
</text> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<!-- 温馨提示 --> |
|||
<view class="tips-card"> |
|||
<view class="tips-title">温馨提示</view> |
|||
<view class="tips-text"> |
|||
1. 请填写真实身份信息,用于办理读者证<br> |
|||
2. 密码请牢记,用于登录和借阅查询<br> |
|||
<!-- 3. 提交后工作人员将尽快审核 --> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import config from '@/utils/config'; |
|||
import { getOpenId } from '@/utils/storage'; |
|||
import { FetchSessionKey, FetchDecryptPhone } from '@/api/user.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
sessionKey: '', |
|||
openid: '', |
|||
formData: { |
|||
name: '', |
|||
idCard: '', |
|||
phone: '', |
|||
password: '', |
|||
confirmPassword: '' |
|||
}, |
|||
showPwd: false, |
|||
showConfirmPwd: false, |
|||
loading: false, |
|||
agreed: false |
|||
}; |
|||
}, |
|||
|
|||
onLoad() { |
|||
this.getSessionKey(); |
|||
}, |
|||
|
|||
methods: { |
|||
async getSessionKey() { |
|||
try { |
|||
uni.login({ |
|||
success: async (loginRes) => { |
|||
console.log('loginRes',loginRes) |
|||
if (loginRes.code) { |
|||
const res = await FetchSessionKey({ |
|||
libcode: config.LIB_CODE, |
|||
code: loginRes.code |
|||
}); |
|||
if (res.code === 200) { |
|||
this.sessionKey = res.data.session_key; |
|||
this.openid = res.data.openid; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
console.error('获取sessionKey失败:', error); |
|||
} |
|||
}, |
|||
|
|||
getPhoneNumber(e) { |
|||
if (e.detail.errMsg !== 'getPhoneNumber:ok') { |
|||
uni.showToast({ title: '取消授权', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
if (!this.sessionKey) { |
|||
uni.showToast({ title: '请稍后再试', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
FetchDecryptPhone({ |
|||
sessionKey: this.sessionKey, |
|||
encryptedData: e.detail.encryptedData, |
|||
iv: e.detail.iv |
|||
}).then(res => { |
|||
if (res.code === 200) { |
|||
this.formData.phone = res.data.phoneNumber || ''; |
|||
uni.showToast({ title: '获取成功', icon: 'success' }); |
|||
} else { |
|||
uni.showToast({ title: res.message || '获取失败', icon: 'none' }); |
|||
} |
|||
}).catch(() => { |
|||
uni.showToast({ title: '获取手机号失败', icon: 'none' }); |
|||
}); |
|||
}, |
|||
togglePwd() { |
|||
this.showPwd = !this.showPwd; |
|||
}, |
|||
toggleConfirmPwd() { |
|||
this.showConfirmPwd = !this.showConfirmPwd; |
|||
}, |
|||
|
|||
// 身份证验证(支持15位和18位,自动去除空格和连字符) |
|||
validateIdCard(idCard) { |
|||
if (!idCard) return false; |
|||
// 去除空格和连字符 |
|||
idCard = idCard.replace(/[\s-]/g, ''); |
|||
// 15位身份证格式验证 |
|||
const idCard15Reg = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/; |
|||
// 18位身份证格式验证 |
|||
const idCard18Reg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/; |
|||
// 15位身份证直接验证格式 |
|||
if (idCard.length === 15) { |
|||
return idCard15Reg.test(idCard); |
|||
} |
|||
// 18位身份证需要验证格式、日期和校验码 |
|||
if (idCard.length !== 18 || !idCard18Reg.test(idCard)) { |
|||
return false; |
|||
} |
|||
// 验证日期有效性(宽松模式:跳过校验码验证) |
|||
const year = +idCard.substring(6, 10); |
|||
const month = +idCard.substring(10, 12); |
|||
const day = +idCard.substring(12, 14); |
|||
const date = new Date(year, month - 1, day); |
|||
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day; |
|||
}, |
|||
// 手机号验证 |
|||
validatePhone(phone) { |
|||
return /^1[3-9]\d{9}$/.test(phone); |
|||
}, |
|||
|
|||
// 表单验证(更严谨) |
|||
validateForm() { |
|||
const { name, idCard, phone, password, confirmPassword } = this.formData; |
|||
|
|||
// 1. 姓名:必须非空且trim后有内容 |
|||
if (!name || name.trim() === '') { |
|||
uni.showToast({ title: '请输入姓名', icon: 'none' }); |
|||
return false; |
|||
} |
|||
|
|||
// 2. 身份证:非空 + 格式正确 |
|||
if (!idCard || idCard.trim() === '') { |
|||
uni.showToast({ title: '请输入身份证号', icon: 'none' }); |
|||
return false; |
|||
} |
|||
if (!this.validateIdCard(idCard.trim())) { |
|||
uni.showToast({ title: '身份证号格式不正确', icon: 'none' }); |
|||
return false; |
|||
} |
|||
|
|||
// 3. 手机号:非空 + 格式正确 |
|||
if (!phone || phone.trim() === '') { |
|||
uni.showToast({ title: '请输入手机号', icon: 'none' }); |
|||
return false; |
|||
} |
|||
if (!this.validatePhone(phone.trim())) { |
|||
uni.showToast({ title: '手机号格式不正确', icon: 'none' }); |
|||
return false; |
|||
} |
|||
|
|||
// 4. 密码:至少6位 |
|||
if (!password || password.length < 6) { |
|||
uni.showToast({ title: '密码至少6位', icon: 'none' }); |
|||
return false; |
|||
} |
|||
|
|||
// 5. 确认密码:必须一致 |
|||
if (password !== confirmPassword) { |
|||
uni.showToast({ title: '两次密码不一致', icon: 'none' }); |
|||
return false; |
|||
} |
|||
|
|||
// 所有校验通过 |
|||
return true; |
|||
}, |
|||
|
|||
// 显示协议弹窗 |
|||
showAgreementModal() { |
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: '请先阅读并同意《爱图智服服务协议》', |
|||
confirmText: '同意', |
|||
cancelText: '不同意', |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
// 用户同意,选中radio并继续提交 |
|||
this.agreed = true; |
|||
this.submitForm(); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 跳转到协议详情页面 |
|||
openAgreement() { |
|||
uni.navigateTo({ |
|||
url: '/subpkg/pages/agreement/agreement' |
|||
}); |
|||
}, |
|||
|
|||
async submit() { |
|||
// 防止重复点击 |
|||
if (this.loading) return; |
|||
|
|||
// 小程序关键:等 input 数据同步到 data |
|||
await this.$nextTick(); |
|||
|
|||
// 校验不通过直接 return |
|||
if (!this.validateForm()) { |
|||
return; |
|||
} |
|||
|
|||
// 如果未同意协议,显示协议弹窗 |
|||
if (!this.agreed) { |
|||
this.showAgreementModal(); |
|||
return; |
|||
} |
|||
|
|||
// 已同意协议,直接提交 |
|||
await this.submitForm(); |
|||
}, |
|||
|
|||
// 实际提交表单 |
|||
async submitForm() { |
|||
this.loading = true; |
|||
try { |
|||
const openId = await getOpenId(); |
|||
if (!openId) { |
|||
uni.showToast({ title: '获取用户信息失败', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
uni.showToast({ title: '后续流程正在研发中,请稍后试用~', icon: 'none' }); |
|||
setTimeout(() => uni.navigateBack(), 1500); |
|||
|
|||
// 提交(trim 后再发) |
|||
// const res = await uni.request({ |
|||
// url: config.baseUrl + '/api/reader/register', |
|||
// method: 'POST', |
|||
// data: { |
|||
// name: this.formData.name.trim(), |
|||
// idCard: this.formData.idCard.trim(), |
|||
// phone: this.formData.phone.trim(), |
|||
// password: this.formData.password, |
|||
// openId, |
|||
// libcode: config.LIB_CODE |
|||
// } |
|||
// }); |
|||
|
|||
// if (res.data?.code === 200) { |
|||
// uni.showToast({ title: '办理成功', icon: 'success' }); |
|||
// setTimeout(() => uni.navigateBack(), 1500); |
|||
// } else { |
|||
// uni.showToast({ title: res.data?.message || '提交失败', icon: 'none' }); |
|||
// } |
|||
} catch (err) { |
|||
console.error('提交异常', err); |
|||
uni.showToast({ title: '网络异常,请重试', icon: 'none' }); |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
page { |
|||
background-color: #f5f7fa; |
|||
} |
|||
.page-container { |
|||
min-height: 100vh; |
|||
background-color: #f5f7fa; |
|||
padding-bottom: 20px; |
|||
} |
|||
|
|||
/* 顶部 */ |
|||
.top-bar { |
|||
height: 210px; |
|||
.library-info { |
|||
position: relative; |
|||
z-index: 2; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
/* 表单卡片 */ |
|||
.form-card { |
|||
margin: -30px 12px 12px; |
|||
background: #fff; |
|||
border-radius: 12px; |
|||
padding: 20px 16px; |
|||
box-shadow: 0 4px 15px rgba(0,0,0,0.08); |
|||
position: relative; |
|||
z-index: 3; |
|||
} |
|||
|
|||
.form-item { |
|||
display: flex; |
|||
align-items: center; |
|||
height: 44px; |
|||
border-bottom: 1rpx solid #f2f3f5; |
|||
margin-bottom: 4px; |
|||
|
|||
.form-icon { |
|||
font-size: 14px; |
|||
color: #01a4fe; |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
.input { |
|||
flex: 1; |
|||
font-size: 14px; |
|||
color: #333; |
|||
} |
|||
|
|||
.pwd-icon { |
|||
color: #999; |
|||
padding: 5px; |
|||
} |
|||
|
|||
.get-phone-btn { |
|||
font-size: 12px; |
|||
color: #fff; |
|||
background-color: #01a4fe; |
|||
border-radius: 20px; |
|||
padding: 6px 10px; |
|||
border: none; |
|||
line-height: 1.2; |
|||
|
|||
&::after { |
|||
border: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 提交按钮 */ |
|||
.submit-btn { |
|||
width: 100%; |
|||
height: 44px; |
|||
background-color: #01a4fe; |
|||
color: #fff; |
|||
border-radius: 22px; |
|||
font-size: 15px; |
|||
margin-top: 20px; |
|||
border: none; |
|||
|
|||
&::after { |
|||
border: none; |
|||
} |
|||
} |
|||
|
|||
/* 同意协议 */ |
|||
.agreement-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-top: 16px; |
|||
|
|||
.radio-box { |
|||
width: 22rpx; |
|||
height: 22rpx; |
|||
border: 2rpx solid #ccc; |
|||
border-radius: 50%; |
|||
margin-right: 10rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: all 0.2s; |
|||
|
|||
&.checked { |
|||
border-color: #01a4fe; |
|||
background-color: #01a4fe; |
|||
} |
|||
|
|||
.radio-inner { |
|||
width: 12rpx; |
|||
height: 12rpx; |
|||
background-color: #fff; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
|
|||
.agreement-text { |
|||
font-size: 12px; |
|||
color: #666; |
|||
flex: 1; |
|||
} |
|||
|
|||
.agreement-link { |
|||
color: #01a4fe; |
|||
} |
|||
} |
|||
|
|||
/* 温馨提示 */ |
|||
.tips-card { |
|||
margin: 0 12px; |
|||
background: #fff; |
|||
border-radius: 10px; |
|||
padding: 15px; |
|||
box-shadow: 0 2px 10px rgba(0,0,0,0.05); |
|||
|
|||
.tips-title { |
|||
font-size: 13px; |
|||
color: #333; |
|||
font-weight: 500; |
|||
margin-bottom: 8px; |
|||
} |
|||
|
|||
.tips-text { |
|||
font-size: 12px; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
} |
|||
} |
|||
</style> |
|||
|
After Width: 1313 | Height: 2335 | Size: 94 KiB |
|
After Width: 1280 | Height: 2637 | Size: 248 KiB |
|
After Width: 1312 | Height: 2716 | Size: 130 KiB |
|
After Width: 1312 | Height: 2716 | Size: 102 KiB |
|
After Width: 1312 | Height: 2716 | Size: 87 KiB |
|
After Width: 1312 | Height: 2703 | Size: 111 KiB |
|
After Width: 1312 | Height: 2716 | Size: 144 KiB |