register.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <template>
  2. <view class="register-container">
  3. <!-- 应用Logo和名称 -->
  4. <view class="app-header">
  5. <view class="app-logo">
  6. <view class="logo-square">
  7. <view class="book-icon">
  8. <view class="book-left"></view>
  9. <view class="book-right"></view>
  10. <view class="book-line"></view>
  11. </view>
  12. </view>
  13. </view>
  14. <text class="app-title">云阅读</text>
  15. <text class="register-title">用户注册</text>
  16. </view>
  17. <!-- 注册表单 -->
  18. <view class="form-container">
  19. <view class="input-group">
  20. <input
  21. class="input-field"
  22. type="text"
  23. placeholder="请输入用户名"
  24. v-model="formData.username"
  25. maxlength="20"
  26. />
  27. <view class="input-line"></view>
  28. </view>
  29. <view class="input-group">
  30. <input
  31. class="input-field"
  32. type="text"
  33. placeholder="请输入昵称(可选)"
  34. v-model="formData.nickname"
  35. maxlength="20"
  36. />
  37. <view class="input-line"></view>
  38. </view>
  39. <view class="input-group">
  40. <input
  41. class="input-field password-input"
  42. :type="showPassword ? 'text' : 'password'"
  43. placeholder="请输入密码(6-20位)"
  44. v-model="formData.password"
  45. maxlength="20"
  46. />
  47. <text
  48. class="toggle-password-btn"
  49. @click="togglePassword"
  50. >
  51. {{ showPassword ? '隐藏' : '显示' }}
  52. </text>
  53. <view class="input-line"></view>
  54. </view>
  55. <view class="input-group">
  56. <input
  57. class="input-field password-input"
  58. :type="showConfirmPassword ? 'text' : 'password'"
  59. placeholder="请确认密码"
  60. v-model="formData.confirmPassword"
  61. maxlength="20"
  62. />
  63. <text
  64. class="toggle-password-btn"
  65. @click="toggleConfirmPassword"
  66. >
  67. {{ showConfirmPassword ? '隐藏' : '显示' }}
  68. </text>
  69. <view class="input-line"></view>
  70. </view>
  71. <view class="input-group">
  72. <input
  73. class="input-field"
  74. type="number"
  75. placeholder="请输入手机号(可选)"
  76. v-model="formData.phone"
  77. maxlength="11"
  78. />
  79. <view class="input-line"></view>
  80. </view>
  81. <view class="input-group">
  82. <input
  83. class="input-field"
  84. type="text"
  85. placeholder="请输入邮箱(可选)"
  86. v-model="formData.email"
  87. maxlength="100"
  88. />
  89. <view class="input-line"></view>
  90. </view>
  91. <button
  92. class="register-btn"
  93. @click="handleRegister"
  94. :disabled="!canRegister"
  95. >
  96. {{ isLoading ? '注册中...' : '注册' }}
  97. </button>
  98. <!-- 返回登录 -->
  99. <view class="login-link" @click="goToLogin">
  100. <text class="login-text">已有账号?立即登录</text>
  101. </view>
  102. </view>
  103. </view>
  104. </template>
  105. <script>
  106. import { register } from '../../utils/api.js'
  107. export default {
  108. data() {
  109. return {
  110. formData: {
  111. username: '',
  112. nickname: '',
  113. password: '',
  114. confirmPassword: '',
  115. phone: '',
  116. email: ''
  117. },
  118. showPassword: false,
  119. showConfirmPassword: false,
  120. isLoading: false
  121. }
  122. },
  123. computed: {
  124. canRegister() {
  125. return this.formData.username.trim().length > 0
  126. && this.formData.password.length >= 6
  127. && this.formData.password === this.formData.confirmPassword
  128. && !this.isLoading
  129. }
  130. },
  131. methods: {
  132. togglePassword() {
  133. this.showPassword = !this.showPassword
  134. },
  135. toggleConfirmPassword() {
  136. this.showConfirmPassword = !this.showConfirmPassword
  137. },
  138. validateForm() {
  139. // 验证用户名
  140. if (!this.formData.username || this.formData.username.trim().length === 0) {
  141. uni.showToast({
  142. title: '请输入用户名',
  143. icon: 'none'
  144. })
  145. return false
  146. }
  147. // 验证密码
  148. if (!this.formData.password || this.formData.password.length < 6) {
  149. uni.showToast({
  150. title: '密码长度不能少于6位',
  151. icon: 'none'
  152. })
  153. return false
  154. }
  155. if (this.formData.password.length > 20) {
  156. uni.showToast({
  157. title: '密码长度不能超过20位',
  158. icon: 'none'
  159. })
  160. return false
  161. }
  162. // 验证确认密码
  163. if (this.formData.password !== this.formData.confirmPassword) {
  164. uni.showToast({
  165. title: '两次输入的密码不一致',
  166. icon: 'none'
  167. })
  168. return false
  169. }
  170. // 验证手机号(如果填写了)
  171. if (this.formData.phone && this.formData.phone.trim().length > 0) {
  172. const phoneReg = /^1[3-9]\d{9}$/
  173. if (!phoneReg.test(this.formData.phone.trim())) {
  174. uni.showToast({
  175. title: '请输入正确的手机号',
  176. icon: 'none'
  177. })
  178. return false
  179. }
  180. }
  181. // 验证邮箱(如果填写了)
  182. if (this.formData.email && this.formData.email.trim().length > 0) {
  183. const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  184. if (!emailReg.test(this.formData.email.trim())) {
  185. uni.showToast({
  186. title: '请输入正确的邮箱地址',
  187. icon: 'none'
  188. })
  189. return false
  190. }
  191. }
  192. return true
  193. },
  194. handleRegister() {
  195. if (!this.validateForm()) {
  196. return
  197. }
  198. // 显示加载中
  199. this.isLoading = true
  200. uni.showLoading({
  201. title: '注册中...',
  202. mask: true
  203. })
  204. // 准备注册数据
  205. const registerData = {
  206. username: this.formData.username.trim(),
  207. password: this.formData.password,
  208. nickname: this.formData.nickname.trim() || this.formData.username.trim(),
  209. phone: this.formData.phone.trim() || null,
  210. email: this.formData.email.trim() || null
  211. }
  212. // 调用后端注册接口
  213. register(registerData)
  214. .then((res) => {
  215. uni.hideLoading()
  216. this.isLoading = false
  217. if (res.code === 200 && res.data) {
  218. uni.showToast({
  219. title: '注册成功',
  220. icon: 'success'
  221. })
  222. // 注册成功后,延迟跳转到登录页面,并传递用户名
  223. setTimeout(() => {
  224. uni.redirectTo({
  225. url: `/pages/login/login?username=${encodeURIComponent(registerData.username)}`
  226. })
  227. }, 1500)
  228. } else {
  229. uni.showToast({
  230. title: res.message || '注册失败',
  231. icon: 'none',
  232. duration: 2000
  233. })
  234. }
  235. })
  236. .catch((err) => {
  237. uni.hideLoading()
  238. this.isLoading = false
  239. console.error('注册失败:', err)
  240. uni.showToast({
  241. title: err.message || '注册失败,请检查网络连接',
  242. icon: 'none',
  243. duration: 2000
  244. })
  245. })
  246. },
  247. goToLogin() {
  248. uni.navigateBack()
  249. }
  250. }
  251. }
  252. </script>
  253. <style scoped>
  254. .register-container {
  255. width: 100%;
  256. min-height: 100vh;
  257. background-color: #FFFFFF;
  258. display: flex;
  259. flex-direction: column;
  260. padding-top: 30px;
  261. box-sizing: border-box;
  262. align-items: center;
  263. padding: 0 60rpx;
  264. box-sizing: border-box;
  265. position: relative;
  266. padding-bottom: 80rpx;
  267. }
  268. .app-header {
  269. display: flex;
  270. flex-direction: column;
  271. align-items: center;
  272. margin-top: 60rpx;
  273. margin-bottom: 60rpx;
  274. flex-shrink: 0;
  275. }
  276. .app-logo {
  277. margin-bottom: 30rpx;
  278. }
  279. .logo-square {
  280. width: 120rpx;
  281. height: 120rpx;
  282. background-color: #4FC3F7;
  283. border-radius: 20rpx;
  284. display: flex;
  285. align-items: center;
  286. justify-content: center;
  287. }
  288. .book-icon {
  289. position: relative;
  290. width: 80rpx;
  291. height: 60rpx;
  292. }
  293. .book-left,
  294. .book-right {
  295. position: absolute;
  296. width: 40rpx;
  297. height: 60rpx;
  298. background-color: #FFFFFF;
  299. border-radius: 3rpx 0 0 3rpx;
  300. }
  301. .book-right {
  302. right: 0;
  303. border-radius: 0 3rpx 3rpx 0;
  304. }
  305. .book-line {
  306. position: absolute;
  307. left: 40rpx;
  308. top: 8rpx;
  309. width: 2rpx;
  310. height: 44rpx;
  311. background-color: #4FC3F7;
  312. }
  313. .app-title {
  314. font-size: 40rpx;
  315. color: #333333;
  316. font-weight: 600;
  317. margin-bottom: 20rpx;
  318. }
  319. .register-title {
  320. font-size: 32rpx;
  321. color: #666666;
  322. font-weight: normal;
  323. }
  324. .form-container {
  325. width: 100%;
  326. flex: 1;
  327. display: flex;
  328. flex-direction: column;
  329. justify-content: flex-start;
  330. }
  331. .input-group {
  332. position: relative;
  333. margin-bottom: 40rpx;
  334. }
  335. .input-field {
  336. width: 100%;
  337. height: 88rpx;
  338. font-size: 30rpx;
  339. padding: 0 20rpx;
  340. color: #333333;
  341. }
  342. .password-input {
  343. padding-right: 80rpx;
  344. }
  345. .toggle-password-btn {
  346. position: absolute;
  347. right: 20rpx;
  348. top: 50%;
  349. transform: translateY(-50%);
  350. font-size: 26rpx;
  351. color: #4FC3F7;
  352. z-index: 10;
  353. padding: 8rpx 12rpx;
  354. }
  355. .input-line {
  356. position: absolute;
  357. bottom: 0;
  358. left: 0;
  359. right: 0;
  360. height: 1rpx;
  361. background-color: #E0E0E0;
  362. }
  363. .register-btn {
  364. width: 100%;
  365. height: 96rpx;
  366. background-color: #E0E0E0;
  367. color: #999999;
  368. font-size: 34rpx;
  369. border-radius: 48rpx;
  370. margin-top: 40rpx;
  371. display: flex;
  372. align-items: center;
  373. justify-content: center;
  374. border: none;
  375. flex-shrink: 0;
  376. }
  377. .register-btn:not([disabled]) {
  378. background-color: #4FC3F7;
  379. color: #FFFFFF;
  380. }
  381. .register-btn[disabled] {
  382. background-color: #E0E0E0;
  383. color: #999999;
  384. }
  385. .login-link {
  386. margin-top: 40rpx;
  387. text-align: center;
  388. }
  389. .login-text {
  390. font-size: 28rpx;
  391. color: #4FC3F7;
  392. }
  393. </style>