forgetPassword.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <template>
  2. <div class="forget-password-container">
  3. <!-- 系统头部 -->
  4. <s-header :name="$t('forgetPassword.header')" :noback="false" />
  5. <!-- 主要内容区域 -->
  6. <div class="forget-content">
  7. <!-- 找回密码表单 -->
  8. <van-form @submit="forgetPasswordSubmit" class="password-form">
  9. <!-- 选择找回方式 -->
  10. <div class="recovery-methods">
  11. <p class="method-title">
  12. {{ $t("forgetPassword.selectForgetPassword") }}
  13. </p>
  14. <van-radio-group
  15. v-model="ifForeign"
  16. direction="vertical"
  17. class="radio-group"
  18. >
  19. <van-radio
  20. name="0"
  21. shape="square"
  22. icon-size="20"
  23. checked-color="@theme-color"
  24. >
  25. <div class="radio-option">
  26. <van-icon name="phone" size="20" class="radio-icon" />
  27. <span>{{ $t("forgetPassword.phone") }}</span>
  28. </div>
  29. </van-radio>
  30. <van-radio
  31. name="1"
  32. shape="square"
  33. icon-size="20"
  34. checked-color="@theme-color"
  35. >
  36. <div class="radio-option">
  37. <van-icon name="envelop-o" size="20" class="radio-icon" />
  38. <span>{{ $t("forgetPassword.email") }}</span>
  39. </div>
  40. </van-radio>
  41. </van-radio-group>
  42. </div>
  43. <!-- 联系方式输入 -->
  44. <div class="contact-input" v-if="ifForeign === '0'">
  45. <p class="method-title">{{ $t("forgetPassword.phoneWordSpan") }}</p>
  46. <van-field
  47. v-model="phone"
  48. name="phone"
  49. type="tel"
  50. :placeholder="$t('forgetPassword.phonePlaceholder')"
  51. :rules="[
  52. { required: true, message: $t('forgetPassword.phoneRequired') },
  53. ]"
  54. class="form-field"
  55. >
  56. <template #left-icon>
  57. <van-icon name="phone-o" size="20" class="field-icon" />
  58. </template>
  59. </van-field>
  60. </div>
  61. <div class="contact-input" v-else>
  62. <p class="method-title">
  63. {{ $t("forgetPassword.emailRequired") }}
  64. </p>
  65. <van-field
  66. v-model="email"
  67. name="email"
  68. :placeholder="$t('forgetPassword.emailPlaceholder')"
  69. :rules="[
  70. { required: true, message: $t('forgetPassword.emailRequired') },
  71. ]"
  72. class="form-field"
  73. >
  74. <template #left-icon>
  75. <van-icon name="envelop-o" size="20" class="field-icon" />
  76. </template>
  77. </van-field>
  78. </div>
  79. <!-- 验证码输入 -->
  80. <div class="verification-code">
  81. <van-field
  82. v-model="code"
  83. name="code"
  84. :placeholder="$t('forgetPassword.codePlaceholder')"
  85. :rules="[
  86. { required: true, message: $t('forgetPassword.codeRequired') },
  87. ]"
  88. class="form-field"
  89. >
  90. <template #left-icon>
  91. <van-icon name="comment-o" size="20" class="field-icon" />
  92. </template>
  93. <template #button>
  94. <van-button
  95. size="small"
  96. class="code-button"
  97. @click="seedVerCode()"
  98. :disabled="time !== 0"
  99. >
  100. {{
  101. time === 0
  102. ? $t("forgetPassword.seedVerCode")
  103. : `${time} s`
  104. }}
  105. </van-button>
  106. </template>
  107. </van-field>
  108. </div>
  109. <!-- 提交按钮 -->
  110. <van-button round block native-type="submit" class="submit-button">
  111. {{ $t("forgetPassword.registerButton") }}
  112. </van-button>
  113. </van-form>
  114. </div>
  115. </div>
  116. </template>
  117. <script>
  118. import { ref, reactive, toRefs, onMounted } from "vue";
  119. import { showToast, showFailToast } from "vant";
  120. import { setLocal, getLocal, styleUrl } from "@/common/js/utils";
  121. import sHeader from "@/components/SimpleHeader";
  122. import { useRouter } from "vue-router";
  123. import { sentForgetCode, checkForgetCode } from "@/service/forgetPassword";
  124. export default {
  125. setup() {
  126. const ifForeign = ref("0"); // 手机号&邮箱状态
  127. const email = ref(""); // 邮箱
  128. const phone = ref(""); // 手机号
  129. const code = ref(""); // 验证码
  130. const verCodeTime = reactive({
  131. time: 0,
  132. }); // 验证码间隔时间及状态
  133. const router = useRouter();
  134. // 发送验证码
  135. const seedVerCode = async () => {
  136. const { data } = await sentForgetCode({
  137. phoneOrEmail: ifForeign.value === "0" ? phone.value : email.value,
  138. });
  139. if (data.code === "00000") {
  140. showToast("验证码发送成功");
  141. verCodeTime.time = 60;
  142. verCodeTimeInterval();
  143. } else {
  144. showFailToast(data.message);
  145. }
  146. };
  147. // 验证码发送成功开始3分钟倒计时
  148. const verCodeTimeInterval = () => {
  149. verCodeTime.time--;
  150. setLocal("forgetVerCodeTime", verCodeTime.time);
  151. if (verCodeTime.time !== 0) {
  152. setTimeout(() => {
  153. verCodeTimeInterval();
  154. }, 1000);
  155. }
  156. };
  157. // 验证-表单
  158. const forgetPasswordSubmit = async () => {
  159. const { data } = await checkForgetCode({
  160. phoneOrEmail: ifForeign.value === "0" ? phone.value : email.value,
  161. code: code.value,
  162. });
  163. if (data.code === "00000") {
  164. showToast("校验成功");
  165. router.push({
  166. path: "/changepassword",
  167. query: { name: data.data },
  168. });
  169. } else {
  170. showFailToast(data.message);
  171. }
  172. };
  173. // 初始化页面获取验证码倒计时
  174. onMounted(async () => {
  175. styleUrl("forgetPassword");
  176. verCodeTime.time = getLocal("forgetVerCodeTime");
  177. if (verCodeTime.time && verCodeTime.time !== "") {
  178. verCodeTime.time = parseInt(verCodeTime.time);
  179. if (verCodeTime.time > 0) {
  180. verCodeTimeInterval();
  181. }
  182. } else {
  183. verCodeTime.time = 0;
  184. }
  185. });
  186. return {
  187. ...toRefs(verCodeTime),
  188. ifForeign,
  189. phone,
  190. email,
  191. code,
  192. seedVerCode,
  193. forgetPasswordSubmit,
  194. };
  195. },
  196. components: { sHeader },
  197. };
  198. </script>
  199. <style lang="less" scoped>
  200. @theme-color: #2c87c8;
  201. .forget-password-container {
  202. display: flex;
  203. flex-direction: column;
  204. height: 100vh;
  205. background: #f5f8ff;
  206. overflow-y: auto;
  207. }
  208. .forget-content {
  209. background: #f5f6fa;
  210. height: calc(100% - 45px);
  211. overflow: auto;
  212. overflow-x: hidden;
  213. }
  214. .password-form {
  215. background-color: #fff;
  216. border-radius: 16px;
  217. padding: 25px;
  218. margin: 15px;
  219. box-shadow: 0 8px 20px rgba(77, 106, 221, 0.12);
  220. }
  221. .form-field {
  222. display: flex;
  223. align-items: center;
  224. text-align: center;
  225. margin-bottom: 20px;
  226. border-radius: 12px;
  227. background-color: #f9faff;
  228. padding: 15px 16px;
  229. :deep(.van-field__control) {
  230. padding-left: 8px;
  231. font-size: 15px;
  232. }
  233. :deep(.van-field__error-message) {
  234. margin-left: 10px;
  235. }
  236. .field-icon {
  237. color: @theme-color;
  238. }
  239. }
  240. .recovery-methods {
  241. margin-bottom: 15px;
  242. .method-title {
  243. font-size: 15px;
  244. color: #1c2b56;
  245. font-weight: 500;
  246. margin-bottom: 12px;
  247. }
  248. }
  249. .radio-group {
  250. .van-radio {
  251. margin-bottom: 12px;
  252. :deep(.van-radio__label) {
  253. flex: 1;
  254. margin-left: 12px;
  255. }
  256. :deep(.van-radio__icon--checked .van-icon) {
  257. background-color: @theme-color;
  258. border-color: @theme-color;
  259. }
  260. }
  261. .radio-option {
  262. display: flex;
  263. align-items: center;
  264. padding: 12px 0;
  265. font-size: 16px;
  266. .radio-icon {
  267. color: @theme-color;
  268. margin-right: 10px;
  269. }
  270. }
  271. }
  272. .contact-input,
  273. .verification-code {
  274. margin-bottom: 5px;
  275. .method-title {
  276. font-size: 15px;
  277. color: #1c2b56;
  278. font-weight: 500;
  279. margin: 15px 0 12px;
  280. }
  281. }
  282. .verification-code {
  283. margin-top: 15px;
  284. }
  285. .code-button {
  286. background-color: @theme-color;
  287. color: white;
  288. border-radius: 20px;
  289. font-size: 14px;
  290. padding: 0 15px;
  291. height: 34px;
  292. &:disabled {
  293. background-color: #ccc;
  294. color: #fff;
  295. }
  296. }
  297. .submit-button {
  298. background: linear-gradient(to right, @theme-color, #6878eb);
  299. color: white;
  300. border: none;
  301. height: 50px;
  302. font-size: 17px;
  303. font-weight: 500;
  304. margin-top: 25px;
  305. box-shadow: 0 4px 15px rgba(77, 106, 221, 0.35);
  306. &:active {
  307. opacity: 0.9;
  308. }
  309. }
  310. .password-footer {
  311. text-align: center;
  312. padding: 25px 0;
  313. font-size: 15px;
  314. color: #666;
  315. .login-link {
  316. color: @theme-color;
  317. font-weight: 500;
  318. margin-left: 5px;
  319. cursor: pointer;
  320. &:hover {
  321. text-decoration: underline;
  322. }
  323. }
  324. }
  325. @media (max-width: 480px) {
  326. .password-form {
  327. padding: 20px 18px;
  328. }
  329. .submit-button {
  330. height: 48px;
  331. font-size: 16px;
  332. }
  333. }
  334. </style>