@@ -7,14 +7,37 @@ import GTitle from './widget/title.vue';
import { useModel } from '@/utils/hooks/useModel' ;
import type { GLoginModalProps , GLogoProps } from './types' ;
import { TransAssetsUrl } from '@/utils/asset' ;
import { computed , ref , watch , reactive , onBeforeUnmount , nextTick , watchEffect , shallowRef , getCurrentInstance , type ComponentInternalInstance } from 'vue' ;
import {
computed ,
ref ,
watch ,
reactive ,
onBeforeUnmount ,
nextTick ,
watchEffect ,
shallowRef ,
getCurrentInstance ,
type ComponentInternalInstance ,
onMounted ,
onUnmounted
} from 'vue' ;
import { Modal } from 'vue-devui/modal' ;
import { Button } from 'vue-devui/button' ;
import { Icon } from 'vue-devui/icon' ;
import { Tooltip } from 'vue-devui/tooltip' ;
import { Message } from 'vue-devui/message' ;
import { verifyRegisterCode , getQuickLoginMsg , getMobileEmailCode , resetUserPassword , checkUsername , registerByMobile , loginByMobile , toLogin } from '@/api/user' ;
import {
verifyRegisterCode ,
getQuickLoginMsg ,
getMobileEmailCode ,
resetUserPassword ,
checkUsername ,
registerByMobile ,
loginByMobile ,
toLogin
} from '@/api/user' ;
import { FormRules , isMobileEmail } from './form' ;
import { useAccountStore } from '@/stores/user' ;
/**
* TODO: 待集成入该hook
@@ -34,7 +57,7 @@ const IconB = TransAssetsUrl(IconSource, 'logo-back');
// 表单验证结果
const formErrors = reactive < Record < string , string > > ( { } ) ;
// 额外验证参数
const extraErrors = reactive < { agreement : string , requestInfo : string } > ( {
const extraErrors = reactive < { agreement : string ; requestInfo : string } > ( {
agreement : '' ,
requestInfo : ''
} ) ;
@@ -45,7 +68,13 @@ const disabled = ref(true);
const loading = ref ( false ) ;
const isRegister = ref ( false ) ;
const cacheForm = reactive < { mobile : string , verificationcode : string , countdownSecond : number , user _id : string , mask : string } > ( {
const cacheForm = reactive < {
mobile : string ;
verificationcode : string ;
countdownSecond : number ;
user _id : string ;
mask : string ;
} > ( {
mobile : '' ,
verificationcode : '' ,
countdownSecond : 59 ,
@@ -53,7 +82,7 @@ const cacheForm = reactive<{ mobile: string, verificationcode: string, countdown
mask : ''
} ) ;
let timer : any = null ;
let interval : any = null ;
let interval : any = null ;
const logos = reactive < GLogoProps [ ] > ( [
{
@@ -99,7 +128,7 @@ const VerifyForm = reactive<FormListProps[]>([
required : true ,
rules : FormRules . codes ,
props : {
' autocomplete' : true ,
autocomplete: true ,
countdown : false ,
second : 59 ,
aliasKey : 'mobile'
@@ -121,13 +150,13 @@ const PasswordForm = reactive<FormListProps[]>([
required : true ,
props : {
'show-password' : true ,
' autocomplete' : true ,
' maxlength' : 30
autocomplete: true ,
maxlength: 30
}
}
] ) ;
const VerifyPassword : FormListProps [ ] = [
const VerifyPassword : FormListProps [ ] = [
{
type : 'input' ,
key : 'password' ,
@@ -137,8 +166,8 @@ const VerifyPassword:FormListProps[] = [
help : '密码最少包含一个大写字母、一个小写字母、一个数字, 长度为8~30之间' ,
props : {
'show-password' : true ,
' autocomplete' : true ,
' maxlength' : 30
autocomplete: true ,
maxlength: 30
}
} ,
{
@@ -175,15 +204,15 @@ const VerifyPassword:FormListProps[] = [
] ,
props : {
'show-password' : true ,
' autocomplete' : true ,
' maxlength' : 30
autocomplete: true ,
maxlength: 30
}
}
] ;
/** 检测用户是否存在 */
const requestCheck = {
trigger : 'change' ,
validator : async ( _ : any , val : string , cb : Function ) => {
validator : async ( _ : any , val : string , cb : Function ) => {
const res = await checkUsername ( val ) ;
if ( ! res . error ) {
if ( res . data . data . result ) {
@@ -223,7 +252,7 @@ const RegisterForm = reactive<FormListProps[]>([
required : true ,
rules : FormRules . codes ,
props : {
' autocomplete' : true ,
autocomplete: true ,
countdown : false ,
second : 59 ,
aliasKey : 'mobile'
@@ -261,17 +290,21 @@ const FormRef = shallowRef<any>(null);
const tabPanel = ref < 'verifyCode' | 'password' > ( 'password' ) ;
const formType = ref < 'login' | 'register' | 'forget' > ( props . defaultType ) ;
const FormConfig = computed < { dict : FormListProps [ ] , showLabel : boolean , layout : 'horizontal' | 'vertical' , title : string , showBack : boolean , button ? : string } > ( ( ) => {
const FormConfig = computed < {
dict : FormListProps [ ] ;
showLabel : boolean ;
layout : 'horizontal' | 'vertical' ;
title : string ;
showBack : boolean ;
button ? : string ;
} > ( ( ) => {
if ( formType . value === 'register' ) {
return {
dict : RegisterForm . map ( item => {
dict : RegisterForm . map ( ( item ) => {
return {
... item ,
defaultValue : item . key === 'mobile'
? cacheForm . mobile
: item . key === 'verificationcode'
? cacheForm . verificationcode
: ''
defaultValue :
item . key === 'mobile' ? cacheForm . mobile : item . key === 'verificationcode' ? cacheForm . verificationcode : ''
} ;
} ) ,
showLabel : true ,
@@ -291,7 +324,7 @@ const FormConfig = computed<{ dict: FormListProps[], showLabel: boolean, layout:
}
if ( tabPanel . value === 'verifyCode' ) {
return {
dict : VerifyForm . map ( item => {
dict : VerifyForm . map ( ( item ) => {
return {
... item ,
defaultValue : item . key === 'mobile' ? cacheForm . mobile : ''
@@ -299,7 +332,7 @@ const FormConfig = computed<{ dict: FormListProps[], showLabel: boolean, layout:
} ) ,
showLabel : false ,
layout : 'vertical' ,
title : '欢迎登录你的GitCode账号' ,
title : '欢迎登录你的GitCode账号1 ' ,
showBack : false
} ;
} else {
@@ -322,7 +355,7 @@ const catchFormErrors = (conf: { errors: Record<string, any[]> }) => {
}
} ;
const handleResetPassword = async ( ) => {
const handleResetPassword = async ( ) => {
if ( FormRef . value ) {
const formData = await FormRef . value . ValidateForm ( ) ;
if ( formData . type === 'success' ) {
@@ -347,7 +380,7 @@ const handleResetPassword = async() => {
}
} ;
const handleConfirm = async ( ) => {
const handleConfirm = async ( ) => {
let agreementFlag = false ;
if ( formType . value === 'forget' ) {
handleResetPassword ( ) ;
@@ -397,7 +430,10 @@ const handleConfirm = async() => {
extraErrors . requestInfo = result . error . error _message ;
}
} else {
const data = tabPanel . value === 'password' ? res : {
const data =
tabPanel . value === 'password'
? res
: {
mobile : res . mobile ,
code : res . code
} ;
@@ -425,7 +461,7 @@ const handleConfirm = async() => {
/**
* 绑定华为云
* @deprecated at 精简单注册登录
*/
*/
// const handleBind = (res: any) => {
// if (!res.error) {
// emits('login', res.data.data);
@@ -440,8 +476,10 @@ const handleClose = (close: Function) => {
} ;
/** 外链url */
const links = {
' agreement' : 'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E6%9C%8D%E5%8A%A1%E6%9D%A1%E6%AC%BE.md' ,
'privacy' : 'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E9 %9A%90%E7%A7%8 1%E6%94%BF %E7 %AD%96 .md'
agreement:
'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E6 %9C%8D%E5%8A%A 1%E6%9D%A1 %E6 %AC%BE .md' ,
privacy :
'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96.md'
} ;
const links _hw = {
@@ -485,22 +523,20 @@ const clearFormError = (key?: string) => {
}
} ;
/** 发送短信相关 */
const handleCountDown = async ( conf : { key : string , value : any , sourceKey : string } ) => {
const handleCountDown = async ( conf : { key : string ; value : any ; sourceKey : string } ) => {
const formData = await FormRef . value . ValidateFormKeys ( [ conf . key ] ) ;
if ( formData . type === 'success' ) {
clearFormError ( conf . key ) ;
if ( tabPanel . value === 'verifyCode' || formType . value === 'register' || formType . value === 'forget' ) {
const res = formType . value === 'login'
const res =
formType . value === 'login'
? await getQuickLoginMsg ( { mobile : conf . value } )
: formType . value === 'forget'
? await getMobileEmailCode ( conf . value )
: await verifyRegisterCode ( { mobile : conf . value , type : 'REGISTER' } ) ;
if ( ! res . error ) {
const source = formType . value === 'register'
? RegisterForm
: formType . value === 'forget'
? ForgetPasswordForm
: VerifyForm ;
const source =
formType . value === 'register' ? RegisterForm : formType . value === 'forget' ? ForgetPasswordForm : VerifyForm ;
cacheForm . mobile = conf . value ;
if ( Number ( res ? . data . data . type === 1 ) ) {
isRegister . value = true ;
@@ -510,7 +546,7 @@ const handleCountDown = async(conf: { key: string, value: any, sourceKey: string
const message = isMobileEmail ( conf . value ) ;
dealLog ( ` 验证码已发送到你的 ${ message } , 请注意查收 ` , 'success' ) ;
}
source . forEach ( item => {
source . forEach ( ( item ) => {
if ( item . key === conf . sourceKey ) {
if ( item . props ) {
item . props . countdown = true ;
@@ -564,7 +600,8 @@ const toCountDown = () => {
} ;
/** 刷新倒计时的状态 */
const refreshCountDown = ( tab : string , menu : string ) => {
const source = menu === 'register'
const source =
menu === 'register'
? RegisterForm
: formType . value === 'forget'
? ForgetPasswordForm
@@ -572,7 +609,7 @@ const refreshCountDown = (tab: string, menu: string) => {
? VerifyForm
: null ;
if ( source ) {
const countdown = source . find ( item => item . props ) ;
const countdown = source . find ( ( item ) => item . props ) ;
if ( countdown ? . props && Object . prototype . hasOwnProperty . call ( countdown . props , 'countdown' ) ) {
if ( cacheForm . countdownSecond !== 59 && menu === 'register' && isRegister . value ) {
countdown . props . second = cacheForm . countdownSecond ;
@@ -584,7 +621,7 @@ const refreshCountDown = (tab: string, menu: string) => {
}
} ;
/** 表单字段change事件 */
const handleFormChange = ( conf : { key : string , errors : Record < string , any > | null , source : Record < string , any > } ) => {
const handleFormChange = ( conf : { key : string ; errors : Record < string , any > | null ; source : Record < string , any > } ) => {
const { key , errors } = conf ;
if ( errors ) {
setFormErrorKey ( { errors } ) ;
@@ -595,9 +632,9 @@ const handleFormChange = (conf: { key: string, errors: Record<string, any> | nul
const keys = Object . keys ( formErrors ) ;
const currentForm = FormConfig . value . dict ;
if ( currentForm ? . length ) {
const obj = currentForm . find ( item => keys . includes ( item . key ) && formErrors [ item . key ] ) ;
const obj = currentForm . find ( ( item ) => keys . includes ( item . key ) && formErrors [ item . key ] ) ;
if ( FormRef . value ) {
const filterKeys = keys . filter ( k => k !== obj ? . key && formErrors [ k ] ) ;
const filterKeys = keys . filter ( ( k ) => k !== obj ? . key && formErrors [ k ] ) ;
if ( filterKeys . length ) {
FormRef . value ? . ClearFormFields ( filterKeys ) ;
} else {
@@ -617,9 +654,9 @@ const errorMsg = computed(() => {
const keys = Object . keys ( formErrors ) ;
const currentForm = FormConfig . value . dict ;
if ( currentForm ? . length ) {
const obj = currentForm . find ( item => keys . includes ( item . key ) && formErrors [ item . key ] ) ;
const obj = currentForm . find ( ( item ) => keys . includes ( item . key ) && formErrors [ item . key ] ) ;
if ( FormRef . value ) {
const filterKeys = keys . filter ( key => key !== obj ? . key && formErrors [ key ] ) ;
const filterKeys = keys . filter ( ( key ) => key !== obj ? . key && formErrors [ key ] ) ;
filterKeys . length && FormRef . value ? . ClearFormFields ( filterKeys ) ;
hackMsgError ( obj ? . key === 'verificationcode' || obj ? . key === 'code' ) ;
}
@@ -644,7 +681,9 @@ const hackMsgError = (show_error: boolean) => {
}
} ;
watch ( ( ) => [ tabPanel . value , formType . value ] , ( newVal , oldVal ) => {
watch (
( ) => [ tabPanel . value , formType . value ] ,
( newVal , oldVal ) => {
if ( FormRef . value ) {
FormRef . value . ClearForm ( ) ;
clearFormError ( ) ;
@@ -662,7 +701,8 @@ watch(() => [tabPanel.value, formType.value], (newVal, oldVal) => {
isRegister . value = false ;
}
refreshCountDown ( tab , menu ) ;
}) ;
}
) ;
// 移除参数
localStorage . removeItem ( 'loginReturnUrl' ) ;
@@ -684,7 +724,9 @@ const handleAuthLogin = (type: 'csdn' | 'gitee' | 'github') => {
// 保存注册上报类型和ref
sessionStorage . setItem ( 'loginType' , props . triggerType || '' ) ;
// window.open('http://localhost:5173/oauth/callback', '_blank', 'width=800, height=800, left=200, top=200');
const url = ` ${ ( import . meta as any ) . env . VITE _API _HOST } ${ ( import . meta as any ) . env . VITE _PASSPORT _PREFIX || '' } /api/v1/oauth/login/ ${ type } ` ;
const url = ` ${ ( import . meta as any ) . env . VITE _API _HOST } ${
( import . meta as any ) . env . VITE _PASSPORT _PREFIX || ''
} /api/v1/oauth/login/ ${ type } ` ;
window . open ( url , '_blank' , 'width=800, height=800, left=400, top=200' ) ;
}
} ;
@@ -707,6 +749,27 @@ onBeforeUnmount(() => {
clearInterval ( interval ) ;
}
} ) ;
onMounted ( ( ) => {
const orgin = 'https://test.gitcode.net' ;
window . addEventListener ( 'message' , async ( event ) => {
if ( event . origin !== orgin ) {
return ; // 安全起见,检查消息来源
}
if ( ! event ? . data ? . access _token ) return ;
const { access _token , refresh _token } = event . data ;
if ( access _token && refresh _token ) {
const account = useAccountStore ( ) ;
await account . checkIsLogin ( access _token , refresh _token ) ;
Message . success ( '登录成功' ) ;
emits ( 'close' , true ) ;
}
} ) ;
} ) ;
onUnmounted ( ( ) => {
window . removeEventListener ( 'message' , ( ) => { } ) ;
} ) ;
< / script >
< template >
@@ -720,95 +783,8 @@ onBeforeUnmount(() => {
:append-to-body = "appendToBody"
: class = "['login-modal', formType === 'login' ? 'login-modal-l' : 'login-modal-r']"
>
< template # header >
< GTitle :icon = "IconB" :title = "FormConfig.title" @back ="formType = 'login'" :show-back = "FormConfig.showBack" class = "login-modal-title" / >
< div v-if = "formType === 'register'" class="text-[14px] leading-[20px] text-G900 font-normal" >
GitCode 与华为云共同为用户提供代码托管服务 , 在使用代码托管相关服务时 , 将同步授权并开通华为云服务 , 并与重庆开源共创科技有限公司 ( GitCode的运营主体 ) 关联及共享 。
< / div >
< / template >
< template # default >
< div class = "flex items-center justify-center mb-[24px] mt-[-2px]" v-if = "formType === 'login'" >
< div : class = "['login-modal-tab mr-[18px]', tabPanel === 'password' ? 'login-modal-tab-active' : 'login-modal-tab-inactive' ]" @click.stop ="tabPanel = 'password'" > 密码登录 < / div >
< div : class = "['login-modal-tab ml-[18px]', tabPanel === 'verifyCode' ? 'login-modal-tab-active' : 'login-modal-tab-inactive' ]" @click.stop ="tabPanel = 'verifyCode'" > 短信登录 < / div >
< / div >
< div class = "text-G900 my-[24px] text-[13px] leading-[18px] px-[8px] tracking-[.5px]" v-if = "formType === 'forget'" > 请通过账号绑定手机号 / 邮箱重置你的账号密码 < / div >
< GForm
:DataList = "FormConfig.dict"
:show-label = "FormConfig.showLabel"
ref = "FormRef"
@ count -down = " handleCountDown "
@change ="handleFormChange"
@complete ="handleFormInput"
:layout = "FormConfig.layout"
size = "lg"
message -type = " none "
: class = "formType === 'register' ? 'mt-[20px] login-form-register-cell' : 'login-form-common-cell'"
>
< template # submit >
< div class = "login-modal-info h-[20px]" v-if = "formType === 'login'" >
< div class = "login-modal-info-left" >
{ { errorMsg } }
< / div >
< div
class = "login-modal-info-right"
@click.stop ="formType = 'forget'"
v-show = "tabPanel === 'password' && formType === 'login'"
>
忘记密码 ?
< / div >
< / div >
< template v-if = "formType === 'login'" >
< div class = "mt-[24px]" >
< Button
color = "primary"
variant = "solid"
size = "lg"
@click ="handleConfirm"
:loading = "loading"
:disabled = "disabled"
class = "login-modal-button"
>
登 录
< / Button >
< div class = "text-[13px] text-[#2562C4] text-center mt-[12px] cursor-pointer leading-[32px]" @click.stop ="formType = 'register'" >
注册新账号
< / div >
< / div >
< / template >
< / template >
< / GForm >
< / template >
< template # footer >
< div class = "login-modal-footer" v-if = "formType === 'login'" >
< GAuth :logos = "logos" @auth ="handleAuthLogin" / >
< div : class = "['login-modal-footer-args', AgreementWarn ? 'shaking-box' : '']" >
< GAgreement v-model = "status" @declares="(typ) => handleDisplay(typ)" / >
< / div >
< / div >
< div v-else-if = "formType === 'register' || formType === 'forget'" >
< Tooltip position = "bottom" :content = "errorMsg" :mouse-enter-delay = "350" : disabled = "!(formType === 'register' && formErrors?.username?.length > 42 )" >
< div class = "text-[#F2050D] text-[14px] break-all box-border leading-[20px] pl-[101px] h-[40px] login-modal-ellipsis" : class = "formType === 'register' && formErrors?.username?.length > 42 ? 'cursor-pointer' : ''" >
{ { errorMsg } }
< / div >
< / Tooltip >
< div : class = "[AgreementWarn ? 'shaking-box' : '']" v-if = "formType === 'register'" >
< GAgreements v-model = "hwStatus" @declares="(typ) => handleDisplay(typ, 'huawei')" agreement-text="《华为云用户协议》" privacy-text="《华为云隐私政策声明》" / >
< GAgreements v-model = "status" @declares="(typ) => handleDisplay(typ)" agreement-text="《GitCode用户协议》" privacy-text="《GitCode隐私政策》" />
< / div >
< div : class = "formType === 'forget' ? ' mt-[44px]' : 'mt-[24px]'" >
< Button
color = "primary"
variant = "solid"
size = "md"
@click ="handleConfirm"
:disabled = "disabled"
:loading = "loading"
class = "w-[100%]"
>
{ { formType === 'forget' ? '修改密码' : '确 认' } }
< / Button >
< / div >
< / div >
< iframe src = "https://test.gitcode.net/login/?be_nested=1" > < / iframe >
< / template >
< / Modal >
< / template >
@@ -818,6 +794,11 @@ $active-title: var(--color-CG900);
$active - color : var ( -- color - CG800 ) ;
$inactive - title : var ( -- color - CG500 ) ;
$active - panel : var ( -- color - G800 ) ;
iframe {
border : 0 ;
width : 100 % ;
height : 60 vh ;
}
. login - modal {
box - sizing : border - box ;
padding : 24 px 28 px 24 px ;
@@ -869,7 +850,7 @@ $active-panel: var(--color-G800);
& : : after {
position : absolute ;
content : '' ;
top : 36 px ;
top : 36 px ;
border - radius : 2 px ;
border - bottom : 2 px solid $active - panel ;
left : - 4 px ;
@@ -893,7 +874,7 @@ $active-panel: var(--color-G800);
height : 20 px ;
}
& - left {
color : # F 2050D ;
color : # f 2050d ;
max - width : 240 px ;
min - width : 240 px ;
overflow : hidden ;
@@ -994,7 +975,8 @@ $active-panel: var(--color-G800);
animation : shaking 0.25 s ease - in - out ;
}
@ keyframes shaking {
0 % , 100 % {
0 % ,
100 % {
transform : translateX ( 0 ) ;
}
25 % {