Login4.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <template>
  2. <div class="main">
  3. <a-form
  4. id="formLogin"
  5. class="user-layout-login"
  6. ref="formLogin"
  7. :form="form"
  8. @submit="handleSubmit"
  9. >
  10. <a-tabs
  11. :activeKey="customActiveKey"
  12. :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset',color:'white' }"
  13. @change="handleTabClick"
  14. >
  15. <a-tab-pane key="tab1" tab="账号密码登录">
  16. <a-alert v-if="isLoginError" type="error" showIcon style="margin-bottom: 24px;" message="账户或密码错误(admin/ant.design )" />
  17. <a-form-item>
  18. <a-input
  19. size="large"
  20. type="text"
  21. placeholder="请输入账户"
  22. v-decorator="[
  23. 'username',
  24. {rules: [{ required: true, message: '请输入帐户名或邮箱地址' }, { validator: handleUsernameOrEmail }], validateTrigger: 'change'}
  25. ]"
  26. >
  27. <a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
  28. </a-input>
  29. </a-form-item>
  30. <a-form-item>
  31. <a-input
  32. size="large"
  33. type="password"
  34. autocomplete="false"
  35. placeholder="请输入密码"
  36. v-decorator="[
  37. 'password',
  38. {rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}
  39. ]"
  40. >
  41. <a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
  42. </a-input>
  43. </a-form-item>
  44. <!-- <a-row :gutter="16" v-if="customActiveKey === 'tab1'">-->
  45. <!-- <a-col class="gutter-row" :span="16">-->
  46. <!-- <a-form-item>-->
  47. <!-- <a-input-->
  48. <!-- size="large"-->
  49. <!-- autocomplete="false"-->
  50. <!-- placeholder="图片验证码"-->
  51. <!-- v-decorator="[-->
  52. <!-- 'captchaCode',-->
  53. <!-- {rules: [{ required: true, message: '图片验证码' }], validateTrigger: 'blur'}-->
  54. <!-- ]"-->
  55. <!-- >-->
  56. <!-- <a-icon slot="prefix" type="picture" :style="{ color: 'rgba(0,0,0,.25)' }"/>-->
  57. <!-- </a-input>-->
  58. <!-- </a-form-item>-->
  59. <!-- </a-col>-->
  60. <!-- <a-col class="gutter-row" :span="8">-->
  61. <!-- <img-->
  62. <!-- class="getCaptcha"-->
  63. <!-- :src="captchaImage"-->
  64. <!-- @click.stop.prevent="getCaptchaImage"-->
  65. <!-- />-->
  66. <!-- </a-col>-->
  67. <!-- </a-row>-->
  68. </a-tab-pane>
  69. <a-tab-pane key="tab2" tab="手机号登录">
  70. <a-form-item>
  71. <a-input size="large" type="text" placeholder="手机号" v-decorator="['mobile', {rules: [{ required: true, pattern: /^1[34578]\d{9}$/, message: '请输入正确的手机号' }], validateTrigger: 'change'}]">
  72. <a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
  73. </a-input>
  74. </a-form-item>
  75. <a-row :gutter="16" v-if="customActiveKey === 'tab2'">
  76. <a-col class="gutter-row" :span="16">
  77. <a-form-item>
  78. <a-input
  79. size="large"
  80. autocomplete="false"
  81. placeholder="图片验证码"
  82. v-decorator="[
  83. 'captchaCode',
  84. {rules: [{ required: true, message: '图片验证码' }], validateTrigger: 'blur'}
  85. ]"
  86. >
  87. <a-icon slot="prefix" type="picture" :style="{ color: 'rgba(0,0,0,.25)' }"/>
  88. </a-input>
  89. </a-form-item>
  90. </a-col>
  91. <a-col class="gutter-row" :span="8">
  92. <img
  93. class="getCaptcha"
  94. :src="captchaImage"
  95. @click.stop.prevent="getCaptchaImage"
  96. />
  97. </a-col>
  98. </a-row>
  99. <a-row :gutter="16">
  100. <a-col class="gutter-row" :span="16">
  101. <a-form-item>
  102. <a-input size="large" type="text" placeholder="短信验证码" v-decorator="['smsCode', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
  103. <a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
  104. </a-input>
  105. </a-form-item>
  106. </a-col>
  107. <a-col class="gutter-row" :span="8">
  108. <a-button
  109. class="getCaptcha"
  110. tabindex="-1"
  111. :disabled="state.smsSendBtn"
  112. @click.stop.prevent="getCaptcha"
  113. v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"
  114. ></a-button>
  115. </a-col>
  116. </a-row>
  117. </a-tab-pane>
  118. </a-tabs>
  119. <a-form-item>
  120. <!-- <a-checkbox v-decorator="['rememberMe']">自动登录</a-checkbox>-->
  121. <!-- <router-link-->
  122. <!-- :to="{ name: 'recover', params: { user: 'aaa'} }"-->
  123. <!-- class="forge-password"-->
  124. <!-- style="float: right;"-->
  125. <!-- >忘记密码</router-link>-->
  126. </a-form-item>
  127. <a-form-item style="margin-top:24px">
  128. <a-button
  129. size="large"
  130. type="primary"
  131. htmlType="submit"
  132. class="login-button"
  133. :loading="state.loginBtn"
  134. :disabled="state.loginBtn"
  135. >确定</a-button>
  136. </a-form-item>
  137. </a-form>
  138. <two-step-captcha
  139. v-if="requiredTwoStepCaptcha"
  140. :visible="stepCaptchaVisible"
  141. @success="stepCaptchaSuccess"
  142. @cancel="stepCaptchaCancel"
  143. ></two-step-captcha>
  144. </div>
  145. </template>
  146. <script>
  147. import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
  148. import { mapActions } from 'vuex'
  149. import { timeFix } from '@/utils/util'
  150. import { getSmsCaptcha, checkCaptchaCode } from '@/api/upms/login'
  151. import { encryptPassword } from '@/common/common'
  152. export default {
  153. components: {
  154. TwoStepCaptcha
  155. },
  156. data () {
  157. return {
  158. customActiveKey: 'tab1',
  159. loginBtn: false,
  160. // login type: 0 email, 1 username, 2 telephone
  161. loginType: 0,
  162. randomStr: '',
  163. captchaImage: null,
  164. isLoginError: false,
  165. requiredTwoStepCaptcha: false,
  166. stepCaptchaVisible: false,
  167. form: this.$form.createForm(this),
  168. state: {
  169. time: 60,
  170. loginBtn: false,
  171. // login type: 0 email, 1 username, 2 telephone
  172. loginType: 0,
  173. smsSendBtn: false
  174. }
  175. }
  176. },
  177. created () {
  178. // get2step({ })
  179. // .then(res => {
  180. // this.requiredTwoStepCaptcha = res.result.stepCode
  181. // })
  182. // .catch(() => {
  183. // this.requiredTwoStepCaptcha = false
  184. // })
  185. // this.requiredTwoStepCaptcha = true
  186. this.getCaptchaImage()
  187. },
  188. methods: {
  189. ...mapActions(['Login', 'Logout']),
  190. // handler
  191. handleUsernameOrEmail (rule, value, callback) {
  192. const { state } = this
  193. const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/
  194. if (regex.test(value)) {
  195. state.loginType = 0
  196. } else {
  197. state.loginType = 1
  198. }
  199. callback()
  200. },
  201. handleTabClick (key) {
  202. this.customActiveKey = key
  203. // this.form.resetFields()
  204. },
  205. handleSubmit (e) {
  206. e.preventDefault()
  207. const {
  208. form: { validateFields },
  209. state,
  210. customActiveKey,
  211. Login
  212. } = this
  213. state.loginBtn = true
  214. const validateFieldsKey = customActiveKey === 'tab1' ? ['username', 'password', 'captchaCode'] : ['mobile', 'smsCode', 'captchaCode']
  215. validateFields(validateFieldsKey, { force: true }, (err, values) => {
  216. if (!err) {
  217. const loginParams = {
  218. ...values,
  219. randomStr: this.randomStr,
  220. password: values.password ? encryptPassword(values.password) : '',
  221. grant_type: 'password',
  222. scope: 'server',
  223. loginType: customActiveKey === 'tab1' ? 1 : 2
  224. }
  225. delete loginParams.username
  226. loginParams[!state.loginType ? 'email' : 'username'] = values.username
  227. // loginParams.password = md5(values.password)
  228. Login(loginParams)
  229. .then((res) => this.loginSuccess(res))
  230. .catch(err => console.log(err))
  231. .finally(() => {
  232. state.loginBtn = false
  233. this.getCaptchaImage()
  234. })
  235. } else {
  236. setTimeout(() => {
  237. state.loginBtn = false
  238. }, 600)
  239. }
  240. })
  241. },
  242. getCaptcha (e) {
  243. e.preventDefault()
  244. const { form: { validateFields }, state } = this
  245. validateFields(['mobile', 'captchaCode'], { force: true }, (err, values) => {
  246. if (!err) {
  247. checkCaptchaCode({ captchaCode: values.captchaCode, randomStr: this.randomStr }).then(res => {
  248. const flag = res.data
  249. if (flag) {
  250. state.smsSendBtn = true
  251. const interval = window.setInterval(() => {
  252. if (state.time-- <= 0) {
  253. state.time = 60
  254. state.smsSendBtn = false
  255. window.clearInterval(interval)
  256. }
  257. }, 1000)
  258. const hide = this.$message.loading('验证码发送中..', 0)
  259. getSmsCaptcha({ mobile: values.mobile, captchaCode: values.captchaCode, randomStr: this.randomStr }).then(res => {
  260. setTimeout(hide, 2500)
  261. this.$notification['success']({
  262. message: '提示',
  263. description: '验证码获取成功,您的验证码为:' + res.data,
  264. duration: 8
  265. })
  266. }).catch(err => {
  267. setTimeout(hide, 1)
  268. clearInterval(interval)
  269. state.time = 60
  270. state.smsSendBtn = false
  271. this.requestFailed(err)
  272. })
  273. } else {
  274. this.$message.error('图片验证码错误')
  275. this.getCaptchaImage()
  276. }
  277. })
  278. }
  279. })
  280. },
  281. stepCaptchaSuccess () {
  282. this.loginSuccess()
  283. },
  284. stepCaptchaCancel () {
  285. this.Logout().then(() => {
  286. this.loginBtn = false
  287. this.stepCaptchaVisible = false
  288. })
  289. },
  290. loginSuccess (res) {
  291. console.log(res)
  292. this.$router.push({ path: '/dashboard/workplace' })
  293. // 延迟 1 秒显示欢迎信息
  294. setTimeout(() => {
  295. this.$notification.success({
  296. message: '欢迎',
  297. description: `${timeFix()},欢迎回来`
  298. })
  299. }, 1000)
  300. this.isLoginError = false
  301. },
  302. requestFailed (err) {
  303. console.log(err)
  304. this.isLoginError = true
  305. this.$notification['error']({
  306. message: '错误',
  307. description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
  308. duration: 4
  309. })
  310. },
  311. getCaptchaImage () {
  312. this.randomStr = this.getRandomStr()
  313. // this.captchaImage = location.protocol + '//' + location.host + '/api/verify/captcha/' + this.randomStr
  314. this.captchaImage = process.env.VUE_APP_API_BASE_URL + '/verify/captcha/' + this.randomStr
  315. // getCaptchaImage(this.randomStr).then(res)
  316. },
  317. getRandomStr () {
  318. return new Date().getTime() + Math.random() * 1000
  319. }
  320. }
  321. }
  322. </script>
  323. <style lang="less" scoped>
  324. .user-layout-login {
  325. label {
  326. font-size: 14px;
  327. }
  328. .getCaptcha {
  329. display: block;
  330. width: 100%;
  331. height: 40px;
  332. }
  333. .forge-password {
  334. font-size: 14px;
  335. }
  336. button.login-button {
  337. padding: 0 15px;
  338. font-size: 16px;
  339. height: 40px;
  340. width: 100%;
  341. }
  342. .ant-tabs-nav {
  343. color:white !important;
  344. }
  345. .user-login-other {
  346. text-align: left;
  347. margin-top: 24px;
  348. line-height: 22px;
  349. .item-icon {
  350. font-size: 24px;
  351. color: rgba(0, 0, 0, 0.2);
  352. margin-left: 16px;
  353. vertical-align: middle;
  354. cursor: pointer;
  355. transition: color 0.3s;
  356. &:hover {
  357. color: #1890ff;
  358. }
  359. }
  360. .register {
  361. float: right;
  362. }
  363. }
  364. }
  365. </style>