index.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div class="login-box">
  3. <div class="login-content" :style="{height: loginType ? '492px' : '430px'}">
  4. <div class="login-header mt-1 ml-1">
  5. <div class="left">
  6. <div ref="phone" :class="['left-qrCode', {'phone-switch': isPhone}]" @click="handlePhone">
  7. <div class="switch-tip">
  8. {{ isPhone ? $t('login.smsOrPassword') : $t('login.scanWeChatCode') }}
  9. </div>
  10. </div>
  11. <div v-if="loginType" class="loginType">
  12. <span>{{ $t('login.enterpriseLogin') }}</span>
  13. <v-tooltip :text="$t('login.switchToPersonalLogin')" location="start">
  14. <template v-slot:activator="{ props }">
  15. <v-btn
  16. class="ml-0"
  17. color="#fffff"
  18. size="x-small"
  19. icon="mdi-swap-vertical"
  20. variant="plain"
  21. v-bind="props"
  22. to="/login"
  23. @click="switchToPersonalLogin"
  24. >
  25. </v-btn>
  26. </template>
  27. </v-tooltip>
  28. <!-- <span class="mdi mdi-swap-vertical ml-4"></span> -->
  29. </div>
  30. </div>
  31. <div class="right mr-2 mt-3" v-if="showClose">
  32. <v-icon color="grey" size="30">mdi-close</v-icon>
  33. </div>
  34. </div>
  35. <div class="login-content-box mt-5">
  36. <div v-if="!isPhone" class="login-tab">
  37. <v-tabs v-model="tab" align-tabs="center" color="primary">
  38. <v-tab :value="1">{{ $t('login.smsLogin') }}</v-tab>
  39. <v-tab :value="2">{{ $t('login.passwordLogin') }}</v-tab>
  40. </v-tabs>
  41. <div v-if="loginType" class="mt-9">
  42. <!-- 企业选择 -->
  43. <companySelect ref="companySelectRef" v-model="enterpriseId"></companySelect>
  44. </div>
  45. <v-window v-model="tab" :class="{'mt-9': !loginType}">
  46. <!-- 验证码登录 -->
  47. <v-window-item :value="1">
  48. <phoneFrom ref="phoneRef" :phoneDisabled="Boolean(loginType)" @handleEnter="handleLogin"></phoneFrom>
  49. </v-window-item>
  50. <!-- 账号密码登录 -->
  51. <v-window-item :value="2">
  52. <passwordFrom ref="passRef" :phoneDisabled="Boolean(loginType)" @handleEnter="handleLogin"></passwordFrom>
  53. </v-window-item>
  54. </v-window>
  55. </div>
  56. <div v-else>
  57. <!-- 微信扫码登录 -->
  58. <div v-if="loginType" class="mt-9">
  59. <companySelect v-model="enterpriseId"></companySelect>
  60. </div>
  61. <qr-code></qr-code>
  62. </div>
  63. <v-btn v-if="!isPhone" :loading="loginLoading" color="primary" class="white--text mt-5" min-width="350" @click="handleLogin">
  64. {{ tab === 1 ? $t('login.loginOrRegister') : $t('login.login') }}
  65. </v-btn>
  66. <div class="login-tips mt-3">
  67. {{ $t('login.agreeLogin') }}
  68. <span class="color" style="cursor: pointer;" @click="handleToUserAgreement">[{{ $t('login.userAgreement') }}]</span>
  69. <span class="color" style="cursor: pointer;" @click="handlePrivacyPolicy">[{{ $t('login.privacyPolicy') }}]</span>
  70. </div>
  71. </div>
  72. </div>
  73. </div>
  74. <!-- <CtDialog :visible="showCompanySelect" title="选择企业" :footer="true" widthType="" @submit="handleSubmit">
  75. <div style="min-height: 10px">
  76. <autocompleteUI></autocompleteUI>
  77. </div>
  78. </CtDialog> -->
  79. </template>
  80. <script setup>
  81. import { ref } from 'vue'
  82. import { getUserBindEnterpriseListByPhone } from '@/api/personal/user'
  83. import passwordFrom from './components/passwordPage.vue'
  84. import phoneFrom from '@/components/VerificationCode'
  85. import qrCode from './components/qrCode.vue'
  86. import companySelect from './components/companySelect.vue'
  87. import { useUserStore } from '@/store/user'
  88. import { useRoute, useRouter } from 'vue-router'
  89. import { useI18n } from '@/hooks/web/useI18n'
  90. import Snackbar from '@/plugins/snackbar'
  91. defineOptions({ name: 'login-index' })
  92. const { t } = useI18n()
  93. const router = useRouter()
  94. const route = useRoute()
  95. const loginType = ref(route.query?.loginType - 0 || null)
  96. const enterpriseId = ref('')
  97. const phone = ref()
  98. let isPhone = ref(false)
  99. const handlePhone = () => {
  100. isPhone.value = !isPhone.value
  101. phone.value.style.backgroundPosition = isPhone.value ? '0 -80px' : '0 0'
  102. }
  103. const tab = ref(1)
  104. const showClose = ref(false)
  105. // 验证码登录
  106. const phoneRef = ref()
  107. const passRef = ref()
  108. const loginLoading = ref(false)
  109. const userStore = useUserStore()
  110. const companySelectRef = ref()
  111. const beforeHandleLogin = async (params) => {
  112. const data = await getUserBindEnterpriseListByPhone({ phone: params.phone}) // 申请通过才会数据,否则空数组
  113. if (companySelectRef.value.item?.items) companySelectRef.value.item.items = data
  114. // localStorage.setItem('companyInfo', JSON.stringify(data))
  115. const bool = Boolean(data?.length)
  116. if (!bool) Snackbar.warning(t('login.loginFailed'))
  117. return bool
  118. }
  119. const handleLogin = async () => {
  120. localStorage.removeItem('currentRole')
  121. const { valid } = tab.value ? await phoneRef.value.phoneForm.validate() : await passRef.value.passwordForm.validate()
  122. if (!valid) return
  123. loginLoading.value = true
  124. try {
  125. const type = loginType.value
  126. let params, api = {}
  127. if (tab.value === 1) { params = { ...phoneRef.value.loginData }; api = 'handleSmsLogin'}
  128. else { params = { ...passRef.value.loginData }; api = 'handlePasswordLogin'}
  129. // 企业登录
  130. if (type === 330) {
  131. const bool = await beforeHandleLogin(params); if (!bool) return
  132. params.loginType = type; params.enterpriseId = enterpriseId.value
  133. }
  134. // await userStore.handlePasswordLogin({ ...passRef.value.loginData, type }) //tab.value === 1
  135. // await userStore.handleSmsLogin({ ...phoneRef.value.loginData, type })
  136. await userStore[api](params)
  137. Snackbar.success(t('login.loginSuccess'))
  138. const path = type === 330 ? '/recruit/enterprise' : '/recruitHome'
  139. router.push({ path })
  140. }
  141. finally {
  142. loginLoading.value = false
  143. }
  144. }
  145. // 隐私、用户协议
  146. const handleToUserAgreement = () => {
  147. router.push({ path: '/userAgreement' })
  148. }
  149. const handlePrivacyPolicy = () => {
  150. router.push({ path: '/privacyPolicy' })
  151. }
  152. const switchToPersonalLogin = () => {
  153. loginType.value = 0
  154. Snackbar.success(t('common.switchSuccessful'))
  155. }
  156. </script>
  157. <style lang="scss" scoped>
  158. .loginType {
  159. position: absolute;
  160. top: 16px;
  161. right: 0;
  162. // width: 100%;
  163. color: #fff;
  164. padding: 4px 15px 4px 32px;
  165. border-radius: 8px 0 0 8px;
  166. // background-color: #ffba5d;
  167. background-color: #fa9c3e;
  168. font-size: 16px;
  169. }
  170. .login-box {
  171. position: relative;
  172. width: 100%;
  173. height: 100%;
  174. background-image: url('https://www.mendunerhr.com/images/userfiles/92d7e4a755e2428b94aab3636d5047f3/images/recruitment/adImages/2018/11/1920x940.jpg');
  175. background-size: cover;
  176. }
  177. .login-content {
  178. position: absolute;
  179. top: 50%;
  180. left: 50%;
  181. translate: -50% -50%;
  182. width: 450px;
  183. // height: 430px;
  184. background-color: #fff;
  185. border-radius: 10px;
  186. }
  187. .login-header {
  188. display: flex;
  189. justify-content: space-between;
  190. align-items: center;
  191. height: 40px;
  192. }
  193. .login-content-box {
  194. padding: 0 50px;
  195. }
  196. .left {
  197. display: flex;
  198. }
  199. .left-qrCode {
  200. position: absolute;
  201. left: 8px;
  202. top: 8px;
  203. width: 40px;
  204. height: 40px;
  205. cursor: pointer;
  206. background: url('../../assets/login.png') 0 0/40px auto no-repeat;
  207. }
  208. .left-qrCode:hover {
  209. background-position: 0 -40px !important;
  210. }
  211. .left-qrCode.phone-switch {
  212. background-position: 0 -80px !important;
  213. }
  214. .left-qrCode.phone-switch:hover {
  215. background-position: 0 -120px !important;
  216. }
  217. .switch-tip {
  218. position: absolute;
  219. left: 60px;
  220. top: 4px;
  221. padding: 0 14px;
  222. border-radius: 4px;
  223. font-size: 12px;
  224. color: var(--color-666);
  225. line-height: 32px;
  226. text-align: center;
  227. background-color: #fff;
  228. box-shadow: 0 6px 13px 0 rgba(0,0,0,.1);
  229. white-space: nowrap;
  230. &::before {
  231. content: '';
  232. position: absolute;
  233. left: -5px;
  234. top: 50%;
  235. transform: translateY(-50%) rotate(45deg);
  236. width: 10px;
  237. height: 10px;
  238. background-color: #fff;
  239. box-shadow: 0 6px 13px 0 rgba(0,0,0,.1);
  240. }
  241. }
  242. .login-tips {
  243. width: 100%;
  244. font-size: 12px;
  245. text-align: center;
  246. }
  247. .color {
  248. color: var(--v-primary-base);
  249. }
  250. </style>