diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 20ec2711c..244f3b9c2 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -144,6 +144,40 @@ class UsersController extends AbstractController return Base::retSuccess($type == 'reg' ? "注册成功" : "登录成功", $user); } + /** + * @api {get} api/users/login/qrcode 02. 二维码登录 + * + * @apiDescription 通过二维码code登录(或:是否登录成功) + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName login__qrcode + * + * @apiParam {String} type 类型 + * - login: 登录(用于:app登录) + * - status: 状态 (默认,用于:网页、客户端获取) + * @apiParam {String} code 二维码 code + * + * @apiSuccess {Number} ret 返回状态码(1需要、0不需要) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function login__qrcode() + { + $type = trim(Request::input('type')); + $code = trim(Request::input('code')); + // + if (strlen($code) < 32) { + return Base::retError("参数错误"); + } + if ($type === 'login') { + $user = User::auth(); + Cache::put("User::qrcode:" . $code, $user->userid, Carbon::now()->addMinute()); + return Base::retSuccess("扫码成功"); + } + // todo 登录成功 + return Base::retError("No identity"); + } + /** * @api {get} api/users/login/needcode 02. 是否需要验证码 * diff --git a/package.json b/package.json index 7ee43f56a..cfa5afd65 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "url": "git+https://github.com/kuaifan/dootask.git" }, "devDependencies": { + "@chenfengyuan/vue-qrcode": "^1.0.2", "axios": "^0.24.0", "cross-env": "^7.0.3", "css-loader": "^6.7.2", diff --git a/resources/assets/js/pages/login.vue b/resources/assets/js/pages/login.vue index 03d512876..ff97c2d02 100644 --- a/resources/assets/js/pages/login.vue +++ b/resources/assets/js/pages/login.vue @@ -4,82 +4,99 @@
+ + - - + - +
@@ -145,8 +162,10 @@ import {mapState} from "vuex"; import {Store} from "le5le-store"; import {languageList, languageType, setLanguage} from "../language"; +import VueQrcode from "@chenfengyuan/vue-qrcode"; export default { + components: {VueQrcode}, data() { return { loadIng: 0, @@ -154,9 +173,14 @@ export default { languageList, languageType, + qrcodeVal: '', + qrcodeTimer: null, + qrcodeLoad: false, + codeNeed: false, codeUrl: $A.apiUrl('users/login/codeimg?_=' + Math.random()), + loginMode: 'access', loginType: 'login', loginJump: false, @@ -189,12 +213,15 @@ export default { this.setServerUrl('').catch(_ => {}); } // + this.qrcodeTimer = setInterval(this.qrcodeStatus, 2000); + // this.subscribe = Store.subscribe('useSSOLogin', () => { this.inputServerUrl(); }); }, beforeDestroy() { + clearInterval(this.qrcodeTimer); if (this.subscribe) { this.subscribe.unsubscribe(); this.subscribe = null; @@ -234,17 +261,35 @@ export default { }, welcomeTitle() { - let title = window.systemInfo.title || "DooTask"; + if (this.loginMode == 'qrcode') { + return this.$L("扫码登录") + } + const title = window.systemInfo.title || "DooTask"; return "Welcome " + title }, + subTitle() { + const title = window.systemInfo.title || "DooTask"; + if (this.loginMode == 'qrcode') { + return this.$L(`请使用${title}移动端扫描二维码。`) + } + if (this.loginType=='reg') { + return this.$L(`输入您的信息以创建帐户。`) + } + return this.$L(`输入您的凭证以访问您的帐户。`) + }, + loginText() { let text = this.loginType == 'login' ? '登录' : '注册'; if (this.loginJump) { text += "成功..." } return text - } + }, + + qrcodeUrl() { + return $A.apiUrl('../login?qrcode=' + this.qrcodeVal) + }, }, watch: { @@ -255,11 +300,14 @@ export default { }) } }, + loginMode() { + this.qrcodeRefresh() + }, loginType(val) { if (val == 'reg') { this.getNeedInvite(); } - } + }, }, methods: { @@ -312,6 +360,36 @@ export default { }); }, + switchLoginMode() { + this.chackServerUrl(true).then(() => { + if (this.loginMode === 'qrcode') { + this.loginMode = 'access' + } else { + this.loginMode = 'qrcode' + } + }) + }, + + qrcodeRefresh() { + if (this.loginMode == 'qrcode') { + this.qrcodeVal = $A.randomString(32) + } + }, + + qrcodeStatus() { + if (this.qrcodeLoad || this.loginMode != 'qrcode') { + return; + } + this.qrcodeLoad = true + this.$store.dispatch("call", { + url: 'users/login/qrcode?code=' + this.qrcodeVal, + }).then(({data}) => { + this.$store.dispatch("handleClearCache", data).then(this.goNext); + }).finally(_ => { + this.qrcodeLoad = false + }); + }, + forgotPassword() { $A.modalWarning("请联系管理员!"); }, @@ -461,11 +539,7 @@ export default { }).then(({data}) => { $A.IDBSave("cacheLoginEmail", this.email) this.codeNeed = false; - this.$store.dispatch("handleClearCache", data).then(() => { - this.goNext(); - }).catch(_ => { - this.goNext(); - }); + this.$store.dispatch("handleClearCache", data).then(this.goNext); }).catch(({data, msg}) => { if (data.code === 'email') { $A.modalWarning(msg); diff --git a/resources/assets/js/pages/manage.vue b/resources/assets/js/pages/manage.vue index 122de443b..6ced330d3 100644 --- a/resources/assets/js/pages/manage.vue +++ b/resources/assets/js/pages/manage.vue @@ -729,8 +729,6 @@ export default { this.$store.dispatch("handleClearCache", null).then(async () => { await $A.IDBSet("clearCache", $A.randomString(6)) $A.reloadUrl() - }).catch(() => { - $A.reloadUrl() }); return; case 'logout': diff --git a/resources/assets/js/pages/manage/setting/index.vue b/resources/assets/js/pages/manage/setting/index.vue index da1f60702..f3c96ab9e 100644 --- a/resources/assets/js/pages/manage/setting/index.vue +++ b/resources/assets/js/pages/manage/setting/index.vue @@ -141,8 +141,6 @@ export default { this.$store.dispatch("handleClearCache", null).then(async () => { await $A.IDBSet("clearCache", $A.randomString(6)) $A.reloadUrl() - }).catch(() => { - $A.reloadUrl() }); break; diff --git a/resources/assets/sass/pages/common.scss b/resources/assets/sass/pages/common.scss index 7243ccd22..4236d1b92 100755 --- a/resources/assets/sass/pages/common.scss +++ b/resources/assets/sass/pages/common.scss @@ -698,3 +698,19 @@ body { 50% { transform: translate3d(-4px, 0, 0); } } +/*登录右侧滑入*/ +.login-mode-enter-active { + transition: all 0.3s ease; +} + +.login-mode-leave-active { + position: absolute; + z-index: -1; + display: none; +} + +.login-mode-enter, +.login-mode-leave-to { + transform: translate(100%, 0); + opacity: 0; +} diff --git a/resources/assets/sass/pages/page-login.scss b/resources/assets/sass/pages/page-login.scss index cbbd639eb..885400f1c 100644 --- a/resources/assets/sass/pages/page-login.scss +++ b/resources/assets/sass/pages/page-login.scss @@ -27,17 +27,58 @@ } } .login-box { + position: relative; margin-top: 36px; width: 400px; max-width: 90%; border-radius: 12px; background-color: #ffffff; box-shadow: 0 0 10px #e6ecfa; + overflow: hidden; + .login-mode-switch { + position: absolute; + top: 4px; + right: 4px; + z-index: 1; + border-radius: 8px; + overflow: hidden; + .login-mode-switch-box { + width: 80px; + height: 80px; + transform: translate(40px,-40px) rotate(45deg); + cursor: pointer; + background-color: rgba($primary-color, 0.8); + transition: background-color .3s; + overflow: hidden; + &:hover { + background-color: $primary-color; + } + .login-mode-switch-icon { + position: absolute; + font-size: 32px; + width: 50px; + height: 50px; + color: #ffffff; + bottom: -20px; + left: 16px; + transform: rotate(-45deg); + display: flex; + align-items: flex-start; + justify-content: flex-start; + > svg { + width: 32px; + height: 32px; + margin-left: 13px; + margin-top: 3px; + } + } + } + } .login-title { font-size: 24px; font-weight: 600; text-align: center; - margin-top: 36px; + margin-top: 46px; } .login-subtitle { font-size: 14px; @@ -46,8 +87,14 @@ padding: 0 12px; color: #AAAAAA; } - .login-input { - margin: 32px 40px; + .login-qrcode { + display: flex; + align-items: center; + justify-content: center; + margin: 52px auto 49px; + } + .login-access { + margin: 26px 40px 30px; > * { margin-top: 26px; } @@ -90,16 +137,6 @@ .login-switch { color: #aaaaaa; } - .login-input-tips-box{ - position: relative; - .login-input-tips{ - font-size: 12px; - position: absolute; - left: 0; - bottom: -20px; - color: #c7c7c7; - } - } > .ivu-poptip { width: 100%; > .ivu-poptip-rel { @@ -179,6 +216,10 @@ background-color: transparent; box-shadow: none; + .login-mode-switch { + display: none; + } + .login-title { font-size: 26px; } @@ -187,7 +228,7 @@ margin-top: 4px; } - .login-input { + .login-access { margin: 20px 36px; .ivu-input-large {