feat 开放平台登录

This commit is contained in:
xxm
2022-11-06 22:46:07 +08:00
parent ae20cfc8c7
commit eedf2e648e
6 changed files with 150 additions and 78 deletions

1
public/icons/weCom.js Normal file
View File

@@ -0,0 +1 @@
!function(e){var t,n,o,c,i,a='<svg><symbol id="wecom" viewBox="0 0 1024 1024"><path d="M700.8256 701.2864c26.88 26.9312 59.392 43.1104 95.4368 53.248l12.1344 3.1232c25.2928 4.608 38.5536 27.648 39.68 51.2 0 27.648-20.1216 51.2-47.7696 55.2448-26.4192 3.4304-54.0672-13.2608-59.8016-39.7312-8.0384-40.8064-25.344-76.4928-55.2448-106.3936-3.4304-3.4816-1.1264-10.9568-2.304-16.6912l13.4656-1.024c2.048 0 3.584 0.256 4.4032 1.024z m48.896-521.216c56.32 59.8016 87.4496 130.56 88.576 212.8384a109.2096 109.2096 0 0 0-45.4656-9.728h-2.304a109.2096 109.2096 0 0 0-104.0896 96.6144 120.2688 120.2688 0 0 0 0 28.7744c-20.1728 20.1216-44.9024 32.768-75.3664 37.9392h-0.6144a105.1648 105.1648 0 0 0-44.288 184.6784 416.3584 416.3584 0 0 1-106.4448 17.8688 481.3824 481.3824 0 0 1-137.472-13.2608 50.0224 50.0224 0 0 0-22.4256 2.304l-104.1408 51.2c-13.2608 6.912-25.344 8.0384-36.864-1.1264-10.9056-8.0896-12.032-20.1728-10.9056-34.56 4.608-28.7232 8.0896-57.4976 10.9568-86.272a31.1296 31.1296 0 0 0-8.0896-18.944c-45.4144-45.5168-84.5312-95.5392-104.0896-158.8224-36.864-125.44-3.4816-232.96 87.4496-322.7648C300.3904 3.9936 590.848 11.4688 749.7216 180.0704z m207.104 414.7712c28.7744 3.4816 50.0224 26.4704 50.0224 55.2448 0 27.648-16.6912 50.0224-44.288 55.2448-33.792 6.144-64.3072 20.48-89.7024 42.496l-9.216 8.704c-5.7856 5.7344-14.3872 10.9056-20.1728 2.304-2.304-4.608-1.1264-15.5648 2.304-18.9952a186.0096 186.0096 0 0 0 51.2-100.7104 54.784 54.784 0 0 1 59.8528-44.288z m-215.1936-51.7632c-1.1264 5.7344 1.1776 14.336-2.304 17.8176-28.7232 28.7744-44.288 63.2832-52.3264 103.0144-5.7856 28.7232-32.256 45.4144-59.8528 40.8064-27.648-4.608-48.3328-28.7744-48.3328-55.808 0-24.1664 17.8688-46.592 41.984-51.2a190.4128 190.4128 0 0 0 103.0144-52.3264c3.4304-3.4816 12.0832-2.304 17.8176-2.304z m51.2-108.1856c25.344 0 48.896 16.6912 54.1184 43.1616 6.8608 39.68 24.1664 74.24 52.3264 102.9632 3.4816 3.4816 1.1776 13.2608 2.304 18.9952-6.912 0-15.5136 2.304-18.944-1.1264-28.8256-29.952-64.512-45.4656-104.192-54.0672a53.6576 53.6576 0 0 1-40.8064-58.7264c3.9936-28.7232 26.4704-50.0224 55.1936-51.2z" ></path></symbol></svg>',d=(d=document.getElementsByTagName("script"))[d.length-1].getAttribute("data-injectcss"),l=function(e,t){t.parentNode.insertBefore(e,t)};if(d&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(e){console&&console.log(e)}}function s(){i||(i=!0,o())}function r(){try{c.documentElement.doScroll("left")}catch(e){return void setTimeout(r,50)}s()}t=function(){var e,t=document.createElement("div");t.innerHTML=a,a=null,(t=t.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",t=t,(e=document.body).firstChild?l(t,e.firstChild):e.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(o=t,c=e.document,i=!1,r(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,s())})}(window);

View File

@@ -1,79 +1,74 @@
<template>
<LoginFormTitle v-show="getShow" class="enter-x" />
<a-form
class="p-4 enter-x"
:model="form"
:validate-trigger="['blur', 'change']"
:rules="rules"
ref="formRef"
v-show="getShow"
@keypress.enter="handleLogin"
>
<a-form-item name="account" class="enter-x">
<a-input size="large" v-model:value="form.account" placeholder="账号/手机号/邮箱" class="fix-auto-fill" />
</a-form-item>
<a-form-item name="password" class="enter-x">
<a-input-password size="large" visibilityToggle v-model:value="form.password" placeholder="密码" />
</a-form-item>
<a-spin :spinning="loading">
<a-form
class="p-4 enter-x"
:model="form"
:validate-trigger="['blur', 'change']"
:rules="rules"
ref="formRef"
v-show="getShow"
@keypress.enter="handleLogin"
>
<a-form-item name="account" class="enter-x">
<a-input size="large" v-model:value="form.account" placeholder="账号/手机号/邮箱" class="fix-auto-fill" />
</a-form-item>
<a-form-item name="password" class="enter-x">
<a-input-password size="large" visibilityToggle v-model:value="form.password" placeholder="密码" />
</a-form-item>
<a-row :span="12" class="enter-x" v-if="loginType.captcha">
<a-col :span="16">
<a-form-item name="captcha" class="enter-x">
<a-input size="large" placeholder="验证码" v-model:value="form.captcha" style="min-width: 100px" />
</a-form-item>
</a-col>
<a-col :span="8" style="text-align: right">
<a-form-item :style="{ 'text-align': 'right', 'margin-left': '20px' }" class="enter-x">
<img style="margin-top: 2px" :src="captchaData" @click="getCaptcha" alt="验证码" />
</a-form-item>
</a-col>
</a-row>
<a-row :span="12" class="enter-x" v-if="loginType.captcha">
<a-col :span="16">
<a-form-item name="captcha" class="enter-x">
<a-input size="large" placeholder="验证码" v-model:value="form.captcha" style="min-width: 100px" />
</a-form-item>
</a-col>
<a-col :span="8" style="text-align: right">
<a-form-item :style="{ 'text-align': 'right', 'margin-left': '20px' }" class="enter-x">
<img style="margin-top: 2px" :src="captchaData" @click="getCaptcha" alt="验证码" />
</a-form-item>
</a-col>
</a-row>
<a-row class="enter-x">
<a-col :span="12">
<a-form-item>
<!-- No logic, you need to deal with it yourself -->
<a-checkbox v-model:checked="rememberMe" size="small"> 记住我 </a-checkbox>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :style="{ 'text-align': 'right' }">
<!-- 没有逻辑你需要自己处理 -->
<a-button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"> 忘记密码? </a-button>
</a-form-item>
</a-col>
</a-row>
<a-row class="enter-x">
<a-col :span="12">
<a-form-item>
<!-- No logic, you need to deal with it yourself -->
<a-checkbox v-model:checked="rememberMe" size="small"> 记住我 </a-checkbox>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :style="{ 'text-align': 'right' }">
<!-- 没有逻辑你需要自己处理 -->
<a-button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"> 忘记密码? </a-button>
</a-form-item>
</a-col>
</a-row>
<a-form-item class="enter-x">
<a-button type="primary" size="large" block @click="handleLogin" :loading="loading"> 登录 </a-button>
</a-form-item>
<a-row class="enter-x">
<a-col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.MOBILE)"> 手机登录 </a-button>
</a-col>
<a-col :md="8" :xs="24" class="!my-2 !md:my-0 xs:mx-0 md:mx-2">
<a-button block @click="setLoginState(LoginStateEnum.QR_CODE)"> 二维码登录 </a-button>
</a-col>
<a-col :md="6" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.REGISTER)"> 注册 </a-button>
</a-col>
</a-row>
<a-divider class="enter-x">其他登录方式</a-divider>
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
<GithubFilled />
<WechatFilled />
<AlipayCircleFilled />
<GoogleCircleFilled />
<TwitterCircleFilled />
</div>
</a-form>
<a-form-item class="enter-x">
<a-button type="primary" size="large" block @click="handleLogin"> 登录 </a-button>
</a-form-item>
<a-row class="enter-x">
<a-col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.MOBILE)"> 手机登录 </a-button>
</a-col>
<a-col :md="8" :xs="24" class="!my-2 !md:my-0 xs:mx-0 md:mx-2">
<a-button block @click="setLoginState(LoginStateEnum.QR_CODE)"> 二维码登录 </a-button>
</a-col>
<a-col :md="6" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.REGISTER)"> 注册 </a-button>
</a-col>
</a-row>
<a-divider class="enter-x">其他登录方式</a-divider>
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
<third-login @loginLoading="loading = true" @loginFinish="loading = false" />
</div>
</a-form>
</a-spin>
</template>
<script lang="ts" setup>
import { reactive, unref, computed, onMounted } from 'vue'
import { GithubFilled, WechatFilled, AlipayCircleFilled, GoogleCircleFilled, TwitterCircleFilled } from '@ant-design/icons-vue'
import LoginFormTitle from './LoginFormTitle.vue'
import { useMessage } from '/@/hooks/web/useMessage'
import { useUserStore } from '/@/store/modules/user'
@@ -85,6 +80,7 @@
import { findLoginTypeByCode, LoginType } from '/@/api/common/LoginAssist'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { LoginParams } from '/@/api/sys/model/userModel'
import ThirdLogin from '/@/views/login/third/ThirdLogin.vue'
const { notification } = useMessage()
const { prefixCls } = useDesign('login')
@@ -138,6 +134,7 @@
findLoginTypeByCode(form.loginType).then(({ data }) => {
loginType = data
if (loginType && loginType.captcha && loginType.enable) {
console.log(loginType)
getCaptcha()
}
})

View File

@@ -4,7 +4,7 @@ export const DING_TALK = 'dingTalk'
export const WE_CHAT = 'weChat'
// 微信(开放平台)
export const WE_CHAT_OPEN = 'weChatOpen'
// 微信
// 企业微信
export const WE_COM = 'weCom'
// QQ
export const QQ = 'qq'

View File

@@ -0,0 +1,66 @@
<template>
<a @click="onThirdLogin(DING_TALK)" title="钉钉"><DingdingOutlined /></a>
<a @click="onThirdLogin(WE_COM)" title="企业微信"> <icon-font class="item-icon" type="wecom" /></a>
<a @click="onThirdLogin(WE_CHAT_OPEN)" title="微信"><WechatFilled /></a>
</template>
<script lang="ts" setup>
import { DingdingOutlined, WechatFilled, createFromIconfontCN } from '@ant-design/icons-vue'
import { $ref } from 'vue/macros'
import { getAppEnvConfig } from '/@/utils/env'
import { useUserStore } from '/@/store/modules/user'
import { LoginParams } from '/@/api/sys/model/userModel'
import { useMessage } from '/@/hooks/web/useMessage'
import { DING_TALK, WE_CHAT_OPEN, WE_COM } from './OpenIdLoginType'
const IconFont = createFromIconfontCN({
scriptUrl: '/icons/weCom.js',
})
const emits = defineEmits(['loginFinish', 'loginLoading'])
const userStore = useUserStore()
const { notification } = useMessage()
const { VITE_GLOB_API_URL, VITE_GLOB_APP_CLIENT } = getAppEnvConfig()
let currentLoginType = $ref<string>()
/**
* 调起登录
*/
function onThirdLogin(loginTpe) {
currentLoginType = loginTpe
const url = VITE_GLOB_API_URL + `/auth/third/toLoginUrl/${loginTpe}`
open(
url,
`login ${loginTpe}`,
'height=600, width=500, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=n o, status=no',
)
// 监听回调
addEventListener('message', authCodeCallback, false)
/**
* 认证码回调
*/
async function authCodeCallback(event) {
// 回调后就进行解绑事件, 防止重复接收消息
window.removeEventListener('message', authCodeCallback)
const data = event.data
emits('loginLoading')
const token = await userStore
.login({
...data,
client: VITE_GLOB_APP_CLIENT,
loginType: currentLoginType,
} as LoginParams)
.finally(() => emits('loginFinish'))
if (token) {
notification.success({
message: '登录成功',
description: '欢迎回来',
duration: 3,
})
}
}
}
</script>
<style scoped></style>

View File

@@ -31,7 +31,7 @@
</template>
</template>
<script lang="ts" setup>
import { reactive, ref, unref, computed, onMounted } from 'vue'
import { reactive, ref, unref, computed, onMounted, watch } from 'vue'
import LoginFormTitle from '../LoginFormTitle.vue'
import { StrengthMeter } from '/@/components/StrengthMeter'
import { useLoginState, LoginStateEnum } from '../useLogin'
@@ -43,7 +43,19 @@
import { useMessage } from '/@/hooks/web/useMessage'
const { handleBackLogin, getLoginState } = useLoginState()
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
const getShow = computed(() => {
const f = unref(getLoginState) === LoginStateEnum.REGISTER
if (f) {
getCaptcha()
}
return f
})
watch(
() => getShow,
(value) => {
console.log(value)
},
)
const { createMessage } = useMessage()
@@ -66,10 +78,6 @@
captcha: [{ required: true, message: '请输入验证码!' }],
} as Record<string, Rule[]>
onMounted(() => {
getCaptcha()
})
/**
* 获取验证码
*/

8
types/config.d.ts vendored
View File

@@ -149,15 +149,15 @@ export interface GlobConfig {
shortName: string
}
export interface GlobEnvConfig {
// Site title
// 项目名称
VITE_GLOB_APP_TITLE: string
// Service interface url
// 接口地址
VITE_GLOB_API_URL: string
// Service interface url prefix
// 接口前缀
VITE_GLOB_API_URL_PREFIX?: string
// Project abbreviation
VITE_GLOB_APP_SHORT_NAME: string
// Upload url
// 上传接口
VITE_GLOB_UPLOAD_URL?: string
// 终端类型
VITE_GLOB_APP_CLIENT: string