login.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <template xmlns="">
  2. <div class="container">
  3. <div class="logo"></div>
  4. <!-- 登录区域 -->
  5. <div class="content">
  6. <!-- 配图 -->
  7. <div class="pic"></div>
  8. <!-- 表单 -->
  9. <div class="field">
  10. <!-- [移动端]标题 -->
  11. <h2 class="mobile-title">
  12. <h3 class="title">芋道后台管理系统</h3>
  13. </h2>
  14. <!-- 表单 -->
  15. <div class="form-cont">
  16. <el-tabs class="form" v-model="loginForm.loginType" style=" float:none;">
  17. <el-tab-pane label="账号密码登录" name="uname">
  18. </el-tab-pane>
  19. <el-tab-pane label="短信验证码登录" name="sms">
  20. </el-tab-pane>
  21. </el-tabs>
  22. <div>
  23. <el-form ref="loginForm" :model="loginForm" :rules="LoginRules" class="login-form">
  24. <el-form-item prop="tenantName" v-if="tenantEnable">
  25. <el-input v-model="loginForm.tenantName" type="text" auto-complete="off" placeholder='租户'>
  26. <svg-icon slot="prefix" icon-class="tree" class="el-input__icon input-icon"/>
  27. </el-input>
  28. </el-form-item>
  29. <!-- 账号密码登录 -->
  30. <div v-if="loginForm.loginType === 'uname'">
  31. <el-form-item prop="username">
  32. <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
  33. <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon"/>
  34. </el-input>
  35. </el-form-item>
  36. <el-form-item prop="password">
  37. <el-input v-model="loginForm.password" type="password" auto-complete="off" placeholder="密码"
  38. @keyup.enter.native="getCode">
  39. <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
  40. </el-input>
  41. </el-form-item>
  42. <el-checkbox v-model="loginForm.rememberMe" style="margin:0 0 25px 0;">记住密码</el-checkbox>
  43. </div>
  44. <!-- 短信验证码登录 -->
  45. <div v-if="loginForm.loginType === 'sms'">
  46. <el-form-item prop="mobile">
  47. <el-input v-model="loginForm.mobile" type="text" auto-complete="off" placeholder="请输入手机号">
  48. <svg-icon slot="prefix" icon-class="phone" class="el-input__icon input-icon"/>
  49. </el-input>
  50. </el-form-item>
  51. <el-form-item prop="mobileCode">
  52. <el-input v-model="loginForm.mobileCode" type="text" auto-complete="off" placeholder="短信验证码"
  53. class="sms-login-mobile-code-prefix"
  54. @keyup.enter.native="handleLogin">
  55. <template>
  56. <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
  57. </template>
  58. <template slot="append">
  59. <span v-if="mobileCodeTimer <= 0" class="getMobileCode" @click="getSmsCode" style="cursor: pointer;">获取验证码</span>
  60. <span v-if="mobileCodeTimer > 0" class="getMobileCode">{{ mobileCodeTimer }}秒后可重新获取</span>
  61. </template>
  62. </el-input>
  63. </el-form-item>
  64. </div>
  65. <!-- 下方的登录按钮 -->
  66. <el-form-item style="width:100%;">
  67. <el-button :loading="loading" size="medium" type="primary" style="width:100%;"
  68. @click.native.prevent="getCode">
  69. <span v-if="!loading">登 录</span>
  70. <span v-else>登 录 中...</span>
  71. </el-button>
  72. </el-form-item>
  73. <!-- 社交登录 -->
  74. <el-form-item style="width:100%;">
  75. <div class="oauth-login" style="display:flex">
  76. <div class="oauth-login-item" v-for="item in SysUserSocialTypeEnum" :key="item.type" @click="doSocialLogin(item)">
  77. <img :src="item.img" height="25px" width="25px" alt="登录" >
  78. <span>{{item.title}}</span>
  79. </div>
  80. </div>
  81. </el-form-item>
  82. <!-- 教程说明 -->
  83. <el-form-item style="width:100%; margin-top:-25px">
  84. <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
  85. <el-link href="https://doc.iocoder.cn/video/" target="_blank" style="padding-left: 10px">🔥视频教程</el-link>
  86. <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank" style="padding-left: 10px">⚡面试手册</el-link>
  87. <el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank" style="padding-left: 10px">🤝外包咨询</el-link>
  88. </el-form-item>
  89. </el-form>
  90. </div>
  91. </div>
  92. </div>
  93. </div>
  94. <!-- 图形验证码 -->
  95. <Verify ref="verify" :captcha-type="'blockPuzzle'" :img-size="{width:'400px',height:'200px'}"
  96. @success="handleLogin" />
  97. <!-- footer -->
  98. <div class="footer">
  99. Copyright © 2020-2022 iocoder.cn All Rights Reserved.
  100. </div>
  101. </div>
  102. </template>
  103. <script>
  104. import {sendSmsCode, socialAuthRedirect} from "@/api/login";
  105. import {getTenantIdByName} from "@/api/system/tenant";
  106. import {SystemUserSocialTypeEnum} from "@/utils/constants";
  107. import {getCaptchaEnable, getTenantEnable} from "@/utils/ruoyi";
  108. import {
  109. getPassword,
  110. getRememberMe, getTenantName,
  111. getUsername,
  112. removePassword, removeRememberMe, removeTenantName,
  113. removeUsername,
  114. setPassword, setRememberMe, setTenantId, setTenantName,
  115. setUsername
  116. } from "@/utils/auth";
  117. import Verify from '@/components/Verifition/Verify';
  118. import {resetUserPwd} from "@/api/system/user";
  119. export default {
  120. name: "Login",
  121. components: {
  122. Verify
  123. },
  124. data() {
  125. return {
  126. codeUrl: "",
  127. captchaEnable: true,
  128. tenantEnable: true,
  129. mobileCodeTimer: 0,
  130. loginForm: {
  131. loginType: "uname",
  132. username: "admin",
  133. password: "admin123",
  134. captchaVerification: "",
  135. mobile: "",
  136. mobileCode: "",
  137. rememberMe: false,
  138. tenantName: "芋道源码",
  139. },
  140. scene: 21,
  141. LoginRules: {
  142. username: [
  143. {required: true, trigger: "blur", message: "用户名不能为空"}
  144. ],
  145. password: [
  146. {required: true, trigger: "blur", message: "密码不能为空"}
  147. ],
  148. mobile: [
  149. {required: true, trigger: "blur", message: "手机号不能为空"},
  150. {
  151. validator: function (rule, value, callback) {
  152. if (/^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/.test(value) === false) {
  153. callback(new Error("手机号格式错误"));
  154. } else {
  155. callback();
  156. }
  157. }, trigger: "blur"
  158. }
  159. ],
  160. tenantName: [
  161. {required: true, trigger: "blur", message: "租户不能为空"},
  162. {
  163. validator: (rule, value, callback) => {
  164. // debugger
  165. getTenantIdByName(value).then(res => {
  166. const tenantId = res.data;
  167. if (tenantId && tenantId >= 0) {
  168. // 设置租户
  169. setTenantId(tenantId)
  170. callback();
  171. } else {
  172. callback('租户不存在');
  173. }
  174. });
  175. },
  176. trigger: 'blur'
  177. }
  178. ]
  179. },
  180. loading: false,
  181. redirect: undefined,
  182. // 枚举
  183. SysUserSocialTypeEnum: SystemUserSocialTypeEnum,
  184. };
  185. },
  186. created() {
  187. // 租户开关
  188. this.tenantEnable = getTenantEnable();
  189. if (this.tenantEnable) {
  190. getTenantIdByName(this.loginForm.tenantName).then(res => { // 设置租户
  191. const tenantId = res.data;
  192. if (tenantId && tenantId >= 0) {
  193. setTenantId(tenantId)
  194. }
  195. });
  196. }
  197. // 验证码开关
  198. this.captchaEnable = getCaptchaEnable();
  199. // 重定向地址
  200. this.redirect = this.$route.query.redirect ? decodeURIComponent(this.$route.query.redirect) : undefined;
  201. this.getCookie();
  202. },
  203. methods: {
  204. getCode() {
  205. // 情况一,未开启:则直接登录
  206. if (!this.captchaEnable) {
  207. this.handleLogin({})
  208. return;
  209. }
  210. // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
  211. // 弹出验证码
  212. this.$refs.verify.show()
  213. },
  214. getCookie() {
  215. const username = getUsername();
  216. const password = getPassword();
  217. const rememberMe = getRememberMe();
  218. const tenantName = getTenantName();
  219. this.loginForm = {
  220. ...this.loginForm,
  221. username: username ? username : this.loginForm.username,
  222. password: password ? password : this.loginForm.password,
  223. rememberMe: rememberMe ? getRememberMe() : false,
  224. tenantName: tenantName ? tenantName : this.loginForm.tenantName,
  225. };
  226. },
  227. handleLogin(captchaParams) {
  228. this.$refs.loginForm.validate(valid => {
  229. if (valid) {
  230. this.loading = true;
  231. // 设置 Cookie
  232. if (this.loginForm.rememberMe) {
  233. setUsername(this.loginForm.username)
  234. setPassword(this.loginForm.password)
  235. setRememberMe(this.loginForm.rememberMe)
  236. setTenantName(this.loginForm.tenantName)
  237. } else {
  238. removeUsername()
  239. removePassword()
  240. removeRememberMe()
  241. removeTenantName()
  242. }
  243. this.loginForm.captchaVerification = captchaParams.captchaVerification
  244. // 发起登陆
  245. // console.log("发起登录", this.loginForm);
  246. this.$store.dispatch(this.loginForm.loginType === "sms" ? "SmsLogin" : "Login", this.loginForm).then(() => {
  247. this.$router.push({path: this.redirect || "/"}).catch(() => {
  248. });
  249. }).catch(() => {
  250. this.loading = false;
  251. });
  252. }
  253. });
  254. },
  255. async doSocialLogin(socialTypeEnum) {
  256. // 设置登录中
  257. this.loading = true;
  258. let tenant = false;
  259. if (this.tenantEnable) {
  260. await this.$prompt('请输入租户名称', "提示", {
  261. confirmButtonText: "确定",
  262. cancelButtonText: "取消"
  263. }).then(async ({value}) => {
  264. await getTenantIdByName(value).then(res => {
  265. const tenantId = res.data;
  266. tenant = true
  267. if (tenantId && tenantId >= 0) {
  268. setTenantId(tenantId)
  269. }
  270. });
  271. }).catch(() => {
  272. // 取消登录按钮 loading状态
  273. this.loading = false;
  274. return false
  275. });
  276. } else {
  277. tenant = true
  278. }
  279. if(tenant){
  280. // 计算 redirectUri
  281. const redirectUri = location.origin + '/social-login?'
  282. + encodeURIComponent('type=' + socialTypeEnum.type + '&redirect=' + (this.redirect || "/")); // 重定向不能丢
  283. // const redirectUri = 'http://127.0.0.1:48080/api/gitee/callback';
  284. // const redirectUri = 'http://127.0.0.1:48080/api/dingtalk/callback';
  285. // 进行跳转
  286. socialAuthRedirect(socialTypeEnum.type, encodeURIComponent(redirectUri)).then((res) => {
  287. // console.log(res.url);
  288. window.location.href = res.data;
  289. });
  290. }
  291. },
  292. /** ========== 以下为升级短信登录 ========== */
  293. getSmsCode() {
  294. if (this.mobileCodeTimer > 0) return;
  295. this.$refs.loginForm.validate(valid => {
  296. if (!valid) return;
  297. sendSmsCode(this.loginForm.mobile, this.scene, this.loginForm.uuid, this.loginForm.code).then(res => {
  298. this.$modal.msgSuccess("获取验证码成功")
  299. this.mobileCodeTimer = 60;
  300. let msgTimer = setInterval(() => {
  301. this.mobileCodeTimer = this.mobileCodeTimer - 1;
  302. if (this.mobileCodeTimer <= 0) {
  303. clearInterval(msgTimer);
  304. }
  305. }, 1000);
  306. });
  307. });
  308. }
  309. }
  310. };
  311. </script>
  312. <style lang="scss" scoped>
  313. @import "~@/assets/styles/login.scss";
  314. .oauth-login {
  315. display: flex;
  316. align-items: center;
  317. cursor:pointer;
  318. }
  319. .oauth-login-item {
  320. display: flex;
  321. align-items: center;
  322. margin-right: 10px;
  323. }
  324. .oauth-login-item img {
  325. height: 25px;
  326. width: 25px;
  327. }
  328. .oauth-login-item span:hover {
  329. text-decoration: underline red;
  330. color: red;
  331. }
  332. .sms-login-mobile-code-prefix {
  333. :deep(.el-input__prefix) {
  334. top: 22%;
  335. }
  336. }
  337. </style>