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 @@
+
+
{{welcomeTitle}}
-
{{$L('输入您的信息以创建帐户。')}}
-
{{$L('输入您的凭证以访问您的帐户。')}}
+
{{$L(subTitle)}}
-
+
@@ -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 {