index.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <template>
  2. <div>
  3. <v-form @submit.prevent ref="phoneForm">
  4. <v-text-field
  5. v-if="props.showEmailInput"
  6. v-model="loginData.email"
  7. placeholder="请输入企业邮箱"
  8. color="primary"
  9. variant="outlined"
  10. density="compact"
  11. validate-on="input"
  12. prepend-inner-icon="mdi-email"
  13. :rules="emailRules"
  14. ></v-text-field>
  15. <v-text-field v-model="loginData.phone" counter="11" :disabled="props.phoneDisabled" :placeholder="$t('login.mobileNumberPlaceholder')" color="primary" variant="outlined" density="compact" :rules="phoneRules" validate-on="input">
  16. <template v-slot:prepend-inner>
  17. <span class="d-flex">
  18. <v-icon icon="mdi-cellphone" size="20"></v-icon>
  19. <!-- <span class="d-flex" id="menu-activator">
  20. <span class="phone-number">{{ currentArea }}</span>
  21. <v-icon size="20">mdi-chevron-down</v-icon>
  22. </span>
  23. <v-menu activator="#menu-activator">
  24. <v-list>
  25. <v-list-item v-for="(item, index) in items" :key="index" :value="index" @click="handleChangeCurrentArea(item)">
  26. <v-list-item-title>{{ item.label }}</v-list-item-title>
  27. </v-list-item>
  28. </v-list>
  29. </v-menu> -->
  30. </span>
  31. </template>
  32. </v-text-field>
  33. <v-text-field
  34. v-model="loginData.code"
  35. :placeholder="$t('login.enterCode')"
  36. color="primary"
  37. variant="outlined"
  38. density="compact"
  39. prepend-inner-icon="mdi-security"
  40. :rules="codeValid"
  41. @keyup.enter="handleEnter"
  42. >
  43. <template #append-inner>
  44. <span v-if="showCode" class="login-code" @click="handleCode">{{ $t('login.getSmsCode') }}</span>
  45. <span v-else class="disable">{{ $t('login.retrieveAgain') }}{{ count }}s</span>
  46. </template>
  47. </v-text-field>
  48. </v-form>
  49. </div>
  50. </template>
  51. <script setup>
  52. defineOptions({ name: 'verification-code' })
  53. import { ref, reactive } from 'vue'
  54. import { setCodeTime } from '@/utils/code'
  55. import { checkEmail } from '@/utils/validate'
  56. import { sendSmsCode } from '@/api/common/index'
  57. import { useI18n } from '@/hooks/web/useI18n'
  58. import Snackbar from '@/plugins/snackbar'
  59. const emits = defineEmits(['handleEnter'])
  60. const { t } = useI18n()
  61. const props = defineProps({
  62. phoneDisabled: Boolean,
  63. phone: String,
  64. showEmailInput: { // 需要输入邮箱
  65. type: Boolean,
  66. default: false
  67. },
  68. scene: { // 短信验证码scene参数: 30-手机号登陆 31-修改手机 32-修改密码 33-忘记密码
  69. type: [Number, String],
  70. default: 30
  71. }
  72. })
  73. const phoneRules = ref([
  74. value => {
  75. if (value) return true
  76. return t('login.mobileNumberPlaceholder')
  77. },
  78. value => {
  79. if (value?.length <= 11 && /^1[3456789]\d{9}$/.test(value)) return true
  80. return t('login.correctPhoneNumber')
  81. }
  82. ])
  83. const codeValid = ref([
  84. value => {
  85. if (value) return true
  86. return '请输入验证码'
  87. },
  88. value => {
  89. if (/^\d{6}$/.test(value)) return true
  90. return '请输入正确格式的六位数字验证码'
  91. }
  92. ])
  93. const emailRules = ref([
  94. value => {
  95. if (value) return true
  96. return props.placeholder ? props.placeholder : '请输入企业邮箱'
  97. },
  98. value => {
  99. if (checkEmail(value)) return true
  100. return '请输入正确的企业邮箱'
  101. }
  102. ])
  103. // 手机号区域
  104. // const currentArea = ref('0086')
  105. // const items = [
  106. // { label: '中国大陆-0086', value: '0086' }
  107. // ]
  108. // const handleChangeCurrentArea = (e) => {
  109. // currentArea.value = e.value
  110. // }
  111. // 获取验证码
  112. const showCode = ref(true)
  113. const count = ref(0)
  114. const timer = ref(null)
  115. const handleCode = () => {
  116. if (!loginData.phone) {
  117. Snackbar.warning(t('login.mobileNumberPlaceholder'))
  118. return
  119. }
  120. count.value = 60
  121. setTime()
  122. getSmsCode()
  123. }
  124. const getSmsCode = async () => {
  125. const query = {
  126. phone: loginData.phone,
  127. scene: props.scene ? props.scene-0 : 30
  128. }
  129. // try {
  130. await sendSmsCode(query)
  131. Snackbar.success(t('login.sendCode'))
  132. // } catch (error) {
  133. // Snackbar.error(error.msg)
  134. // }
  135. }
  136. const setTime = () => {
  137. showCode.value = false
  138. timer.value = setInterval(() => {
  139. let number = count.value
  140. if (number > 0 && number <= 60) {
  141. count.value--
  142. setCodeTime(number - 1)
  143. } else {
  144. showCode.value = true
  145. clearInterval(timer.value)
  146. timer.value = null
  147. }
  148. }, 1000)
  149. }
  150. const autoTimer = () => {
  151. count.value = 0
  152. if(!count.value) return
  153. setTime()
  154. }
  155. autoTimer()
  156. const loginUserPhone = localStorage.getItem('loginUserPhone') || ''
  157. const loginData = reactive({
  158. email: '',
  159. phone: loginUserPhone,
  160. code: ''
  161. })
  162. if (props.phone) loginData.phone = props.phone
  163. const phoneForm = ref()
  164. const handleEnter = () => {
  165. emits('handleEnter')
  166. }
  167. const resetPhone = () => {
  168. loginData.phone = ''
  169. loginData.code = ''
  170. count.value = 0
  171. }
  172. defineExpose({
  173. resetPhone,
  174. loginData,
  175. phoneForm
  176. })
  177. </script>
  178. <style lang="scss" scoped>
  179. .login-code {
  180. width: 97px;
  181. color: var(--v-primary-base);
  182. text-align: end;
  183. font-size: 12px;
  184. cursor: pointer;
  185. }
  186. .disable {
  187. width: 72px;
  188. color: grey;
  189. font-size: 12px;
  190. }
  191. .phone-number {
  192. width: 34px;
  193. font-size: 12px;
  194. }
  195. </style>