图书馆小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
11 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="page-container">
  3. <!-- 顶部背景 + 标题 -->
  4. <view class="top-bar">
  5. <image class="top-bar-bg" src="@/static/images/mingqi-beij@2x.png" mode="aspectFill"></image>
  6. <view class="library-info">
  7. <image class="avatar" src="@/static/images/logo.jpg" mode="aspectFill"></image>
  8. <view class="library-name">葛店经济技术开发区图书馆</view>
  9. <view class="sub-title">读者证办理</view>
  10. </view>
  11. </view>
  12. <!-- 表单卡片 -->
  13. <view class="form-card">
  14. <!-- 姓名 -->
  15. <view class="form-item">
  16. <uni-icons class="form-icon" custom-prefix="iconfont" type="icon-xingming" size="22"></uni-icons>
  17. <input
  18. class="input"
  19. placeholder="请输入真实姓名"
  20. v-model="formData.name"
  21. maxlength="20"
  22. />
  23. </view>
  24. <!-- 身份证号 -->
  25. <view class="form-item">
  26. <uni-icons class="form-icon" custom-prefix="iconfont" type="icon-duzhezheng" size="24"></uni-icons>
  27. <input
  28. class="input"
  29. placeholder="请输入18位身份证号"
  30. v-model="formData.idCard"
  31. maxlength="18"
  32. />
  33. </view>
  34. <!-- 手机号 -->
  35. <view class="form-item">
  36. <uni-icons class="form-icon" type="phone" size="24"></uni-icons>
  37. <input
  38. class="input"
  39. placeholder="请输入手机号"
  40. v-model="formData.phone"
  41. type="number"
  42. maxlength="11"
  43. />
  44. <button class="get-phone-btn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
  45. 一键获取
  46. </button>
  47. </view>
  48. <!-- 密码 -->
  49. <view class="form-item">
  50. <uni-icons class="form-icon" type="locked" size="24"></uni-icons>
  51. <input
  52. class="input"
  53. placeholder="请设置6-20位密码"
  54. :password="!showPwd"
  55. v-model="formData.password"
  56. maxlength="20"
  57. />
  58. <uni-icons
  59. class="pwd-icon"
  60. :type="showPwd ? 'eye' : 'eye-slash'"
  61. size="20"
  62. @click="togglePwd"
  63. ></uni-icons>
  64. </view>
  65. <!-- 确认密码 -->
  66. <view class="form-item">
  67. <uni-icons class="form-icon" type="locked" size="24"></uni-icons>
  68. <input
  69. class="input"
  70. placeholder="请再次确认密码"
  71. :password="!showConfirmPwd"
  72. v-model="formData.confirmPassword"
  73. maxlength="20"
  74. />
  75. <uni-icons
  76. class="pwd-icon"
  77. :type="showConfirmPwd ? 'eye' : 'eye-slash'"
  78. size="20"
  79. @click="toggleConfirmPwd"
  80. ></uni-icons>
  81. </view>
  82. <!-- 提交按钮 -->
  83. <button class="submit-btn" type="primary" @click="submit" :loading="loading">
  84. {{ loading ? '提交中...' : '确认办理' }}
  85. </button>
  86. </view>
  87. <!-- 温馨提示 -->
  88. <view class="tips-card">
  89. <view class="tips-title">温馨提示</view>
  90. <view class="tips-text">
  91. 1. 请填写真实身份信息用于办理读者证<br>
  92. 2. 密码请牢记用于登录和借阅查询<br>
  93. <!-- 3. 提交后工作人员将尽快审核 -->
  94. </view>
  95. </view>
  96. </view>
  97. </template>
  98. <script>
  99. import config from '@/utils/config';
  100. import { getOpenId } from '@/utils/storage';
  101. export default {
  102. data() {
  103. return {
  104. formData: {
  105. name: '',
  106. idCard: '',
  107. phone: '',
  108. password: '',
  109. confirmPassword: ''
  110. },
  111. showPwd: false,
  112. showConfirmPwd: false,
  113. loading: false
  114. };
  115. },
  116. onLoad() {},
  117. methods: {
  118. getPhoneNumber(e) {
  119. if (e.detail.errMsg !== 'getPhoneNumber:ok') {
  120. uni.showToast({ title: '取消授权', icon: 'none' });
  121. return;
  122. }
  123. // #ifdef MP-WEIXIN
  124. uni.login({
  125. success: (loginRes) => {
  126. uni.request({
  127. url: config.baseUrl + '/api/wx/decryptPhone',
  128. method: 'POST',
  129. data: {
  130. code: loginRes.code,
  131. encryptedData: e.detail.encryptedData,
  132. iv: e.detail.iv
  133. },
  134. success: (res) => {
  135. if (res.data?.code === 200) {
  136. this.formData.phone = res.data.data.phoneNumber || '';
  137. } else {
  138. uni.showToast({ title: res.data?.message || '获取失败', icon: 'none' });
  139. }
  140. },
  141. fail: () => uni.showToast({ title: '获取手机号失败', icon: 'none' })
  142. });
  143. },
  144. fail: () => uni.showToast({ title: '登录失败', icon: 'none' })
  145. });
  146. // #endif
  147. // #ifndef MP-WEIXIN
  148. uni.showToast({ title: '仅支持微信小程序', icon: 'none' });
  149. // #endif
  150. },
  151. togglePwd() {
  152. this.showPwd = !this.showPwd;
  153. },
  154. toggleConfirmPwd() {
  155. this.showConfirmPwd = !this.showConfirmPwd;
  156. },
  157. // 身份证验证(支持15位和18位,自动去除空格和连字符)
  158. validateIdCard(idCard) {
  159. if (!idCard) return false;
  160. // 去除空格和连字符
  161. idCard = idCard.replace(/[\s-]/g, '');
  162. // 15位身份证格式验证
  163. const idCard15Reg = /^[1-9]\d{5}\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}$/;
  164. // 18位身份证格式验证
  165. 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]$/;
  166. // 15位身份证直接验证格式
  167. if (idCard.length === 15) {
  168. return idCard15Reg.test(idCard);
  169. }
  170. // 18位身份证需要验证格式、日期和校验码
  171. if (idCard.length !== 18 || !idCard18Reg.test(idCard)) {
  172. return false;
  173. }
  174. // 验证日期有效性(宽松模式:跳过校验码验证)
  175. const year = +idCard.substring(6, 10);
  176. const month = +idCard.substring(10, 12);
  177. const day = +idCard.substring(12, 14);
  178. const date = new Date(year, month - 1, day);
  179. return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
  180. },
  181. // 手机号验证
  182. validatePhone(phone) {
  183. return /^1[3-9]\d{9}$/.test(phone);
  184. },
  185. // ✅ 【核心修复】表单验证(更严谨)
  186. validateForm() {
  187. const { name, idCard, phone, password, confirmPassword } = this.formData;
  188. // 1. 姓名:必须非空且trim后有内容
  189. if (!name || name.trim() === '') {
  190. uni.showToast({ title: '请输入姓名', icon: 'none' });
  191. return false;
  192. }
  193. // 2. 身份证:非空 + 格式正确
  194. if (!idCard || idCard.trim() === '') {
  195. uni.showToast({ title: '请输入身份证号', icon: 'none' });
  196. return false;
  197. }
  198. if (!this.validateIdCard(idCard.trim())) {
  199. uni.showToast({ title: '身份证号格式不正确', icon: 'none' });
  200. return false;
  201. }
  202. // 3. 手机号:非空 + 格式正确
  203. if (!phone || phone.trim() === '') {
  204. uni.showToast({ title: '请输入手机号', icon: 'none' });
  205. return false;
  206. }
  207. if (!this.validatePhone(phone.trim())) {
  208. uni.showToast({ title: '手机号格式不正确', icon: 'none' });
  209. return false;
  210. }
  211. // 4. 密码:至少6位
  212. if (!password || password.length < 6) {
  213. uni.showToast({ title: '密码至少6位', icon: 'none' });
  214. return false;
  215. }
  216. // 5. 确认密码:必须一致
  217. if (password !== confirmPassword) {
  218. uni.showToast({ title: '两次密码不一致', icon: 'none' });
  219. return false;
  220. }
  221. // 所有校验通过
  222. return true;
  223. },
  224. async submit() {
  225. // 防止重复点击
  226. if (this.loading) return;
  227. // 小程序关键:等 input 数据同步到 data
  228. await this.$nextTick();
  229. // 校验不通过直接 return
  230. if (!this.validateForm()) {
  231. return;
  232. }
  233. this.loading = true;
  234. try {
  235. const openId = await getOpenId();
  236. if (!openId) {
  237. uni.showToast({ title: '获取用户信息失败', icon: 'none' });
  238. return;
  239. }
  240. uni.showToast({ title: '后续流程正在研发中,请稍后试用~', icon: 'none' });
  241. setTimeout(() => uni.navigateBack(), 1500);
  242. // 提交(trim 后再发)
  243. // const res = await uni.request({
  244. // url: config.baseUrl + '/api/reader/register',
  245. // method: 'POST',
  246. // data: {
  247. // name: this.formData.name.trim(),
  248. // idCard: this.formData.idCard.trim(),
  249. // phone: this.formData.phone.trim(),
  250. // password: this.formData.password,
  251. // openId,
  252. // libcode: config.LIB_CODE
  253. // }
  254. // });
  255. // if (res.data?.code === 200) {
  256. // uni.showToast({ title: '办理成功', icon: 'success' });
  257. // setTimeout(() => uni.navigateBack(), 1500);
  258. // } else {
  259. // uni.showToast({ title: res.data?.message || '提交失败', icon: 'none' });
  260. // }
  261. } catch (err) {
  262. console.error('提交异常', err);
  263. uni.showToast({ title: '网络异常,请重试', icon: 'none' });
  264. } finally {
  265. this.loading = false;
  266. }
  267. }
  268. }
  269. };
  270. </script>
  271. <style lang="scss" scoped>
  272. page {
  273. background-color: #f5f7fa;
  274. }
  275. .page-container {
  276. min-height: 100vh;
  277. background-color: #f5f7fa;
  278. padding-bottom: 20px;
  279. }
  280. /* 顶部 */
  281. .top-bar {
  282. height: 210px;
  283. .library-info {
  284. position: relative;
  285. z-index: 2;
  286. color: #fff;
  287. }
  288. }
  289. /* 表单卡片 */
  290. .form-card {
  291. margin: -30px 12px 12px;
  292. background: #fff;
  293. border-radius: 12px;
  294. padding: 20px 16px;
  295. box-shadow: 0 4px 15px rgba(0,0,0,0.08);
  296. position: relative;
  297. z-index: 3;
  298. }
  299. .form-item {
  300. display: flex;
  301. align-items: center;
  302. height: 44px;
  303. border-bottom: 1rpx solid #f2f3f5;
  304. margin-bottom: 4px;
  305. .form-icon {
  306. font-size: 14px;
  307. color: #01a4fe;
  308. margin-right: 10px;
  309. }
  310. .input {
  311. flex: 1;
  312. font-size: 14px;
  313. color: #333;
  314. }
  315. .pwd-icon {
  316. color: #999;
  317. padding: 5px;
  318. }
  319. .get-phone-btn {
  320. font-size: 12px;
  321. color: #fff;
  322. background-color: #01a4fe;
  323. border-radius: 20px;
  324. padding: 6px 10px;
  325. border: none;
  326. line-height: 1.2;
  327. &::after {
  328. border: none;
  329. }
  330. }
  331. }
  332. /* 提交按钮 */
  333. .submit-btn {
  334. width: 100%;
  335. height: 44px;
  336. background-color: #01a4fe;
  337. color: #fff;
  338. border-radius: 22px;
  339. font-size: 15px;
  340. margin-top: 20px;
  341. border: none;
  342. &::after {
  343. border: none;
  344. }
  345. }
  346. /* 温馨提示 */
  347. .tips-card {
  348. margin: 0 12px;
  349. background: #fff;
  350. border-radius: 10px;
  351. padding: 15px;
  352. box-shadow: 0 2px 10px rgba(0,0,0,0.05);
  353. .tips-title {
  354. font-size: 13px;
  355. color: #333;
  356. font-weight: 500;
  357. margin-bottom: 8px;
  358. }
  359. .tips-text {
  360. font-size: 12px;
  361. color: #666;
  362. line-height: 1.6;
  363. }
  364. }
  365. </style>