Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/components/Header/UserAvatar.vue
This commit is contained in:
付民康
2025-03-14 16:34:10 +08:00
5 changed files with 231 additions and 192 deletions

View File

@@ -52,13 +52,18 @@ const toggleDropDown = (isShow) => {
} }
}; };
const logout = async() => { const logout = async () => {
RemoveInfo(); // 目前先强制清除用户信息,后面接口通了再调
const res = await toLogout(); const res = await toLogout();
await reqCatch(devLogout, {}); // 清除社区登录 await reqCatch(devLogout, {}); // 清除社区登录
if (!res.error) { if (!res.error) {
RemoveInfo(); RemoveInfo();
// 有权限刷新当前页,无权限需要登录返回首页 // 有权限刷新当前页,无权限需要登录返回首页
if (route.meta?.needLogin || (route.meta?.type === 'repo' && repoInfo.value?.visibility === 'private') || orgInfo.value?.visibility === 'private') { if (
route.meta?.needLogin ||
(route.meta?.type === 'repo' && repoInfo.value?.visibility === 'private') ||
orgInfo.value?.visibility === 'private'
) {
router.push('/'); router.push('/');
} else { } else {
router.go(0); router.go(0);
@@ -84,11 +89,10 @@ const DEFAULT_ROUTER_PARAMS = Object.freeze({
params: { params: {
namespace: accountInfo.value.username namespace: accountInfo.value.username
}, },
query: { query: {}
}
}); });
// 获取router跳转参数 // 获取router跳转参数
const getRouterLink = params => { const getRouterLink = (params) => {
if (typeof params === 'string') { if (typeof params === 'string') {
return { return {
...DEFAULT_ROUTER_PARAMS, ...DEFAULT_ROUTER_PARAMS,
@@ -156,7 +160,7 @@ const blockList = ref([
justify-content: space-between; justify-content: space-between;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
color: #2D2D2E; color: #2d2d2e;
line-height: 20px; line-height: 20px;
.close-icon { .close-icon {
cursor: pointer; cursor: pointer;
@@ -164,7 +168,7 @@ const blockList = ref([
} }
&-id { &-id {
font-size: 12px; font-size: 12px;
color: #707A87; color: #707a87;
line-height: 16px; line-height: 16px;
} }
} }
@@ -183,7 +187,6 @@ const blockList = ref([
} }
} }
} }
</style> </style>
<style> <style>

View File

@@ -7,14 +7,37 @@ import GTitle from './widget/title.vue';
import { useModel } from '@/utils/hooks/useModel'; import { useModel } from '@/utils/hooks/useModel';
import type { GLoginModalProps, GLogoProps } from './types'; import type { GLoginModalProps, GLogoProps } from './types';
import { TransAssetsUrl } from '@/utils/asset'; 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 { Modal } from 'vue-devui/modal';
import { Button } from 'vue-devui/button'; import { Button } from 'vue-devui/button';
import { Icon } from 'vue-devui/icon'; import { Icon } from 'vue-devui/icon';
import { Tooltip } from 'vue-devui/tooltip'; import { Tooltip } from 'vue-devui/tooltip';
import { Message } from 'vue-devui/message'; 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 { FormRules, isMobileEmail } from './form';
import { useAccountStore } from '@/stores/user';
/** /**
* TODO: 待集成入该hook * TODO: 待集成入该hook
@@ -34,7 +57,7 @@ const IconB = TransAssetsUrl(IconSource, 'logo-back');
// 表单验证结果 // 表单验证结果
const formErrors = reactive<Record<string, string>>({}); const formErrors = reactive<Record<string, string>>({});
// 额外验证参数 // 额外验证参数
const extraErrors = reactive<{ agreement: string, requestInfo: string }>({ const extraErrors = reactive<{ agreement: string; requestInfo: string }>({
agreement: '', agreement: '',
requestInfo: '' requestInfo: ''
}); });
@@ -45,7 +68,13 @@ const disabled = ref(true);
const loading = ref(false); const loading = ref(false);
const isRegister = 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: '', mobile: '',
verificationcode: '', verificationcode: '',
countdownSecond: 59, countdownSecond: 59,
@@ -53,7 +82,7 @@ const cacheForm = reactive<{ mobile: string, verificationcode: string, countdown
mask: '' mask: ''
}); });
let timer: any = null; let timer: any = null;
let interval:any = null; let interval: any = null;
const logos = reactive<GLogoProps[]>([ const logos = reactive<GLogoProps[]>([
{ {
@@ -99,7 +128,7 @@ const VerifyForm = reactive<FormListProps[]>([
required: true, required: true,
rules: FormRules.codes, rules: FormRules.codes,
props: { props: {
'autocomplete': true, autocomplete: true,
countdown: false, countdown: false,
second: 59, second: 59,
aliasKey: 'mobile' aliasKey: 'mobile'
@@ -121,13 +150,13 @@ const PasswordForm = reactive<FormListProps[]>([
required: true, required: true,
props: { props: {
'show-password': true, 'show-password': true,
'autocomplete': true, autocomplete: true,
'maxlength': 30 maxlength: 30
} }
} }
]); ]);
const VerifyPassword:FormListProps[] = [ const VerifyPassword: FormListProps[] = [
{ {
type: 'input', type: 'input',
key: 'password', key: 'password',
@@ -137,8 +166,8 @@ const VerifyPassword:FormListProps[] = [
help: '密码最少包含一个大写字母、一个小写字母、一个数字, 长度为8~30之间', help: '密码最少包含一个大写字母、一个小写字母、一个数字, 长度为8~30之间',
props: { props: {
'show-password': true, 'show-password': true,
'autocomplete': true, autocomplete: true,
'maxlength': 30 maxlength: 30
} }
}, },
{ {
@@ -175,15 +204,15 @@ const VerifyPassword:FormListProps[] = [
], ],
props: { props: {
'show-password': true, 'show-password': true,
'autocomplete': true, autocomplete: true,
'maxlength': 30 maxlength: 30
} }
} }
]; ];
/** 检测用户是否存在 */ /** 检测用户是否存在 */
const requestCheck = { const requestCheck = {
trigger: 'change', trigger: 'change',
validator: async(_: any, val: string, cb: Function) => { validator: async (_: any, val: string, cb: Function) => {
const res = await checkUsername(val); const res = await checkUsername(val);
if (!res.error) { if (!res.error) {
if (res.data.data.result) { if (res.data.data.result) {
@@ -223,7 +252,7 @@ const RegisterForm = reactive<FormListProps[]>([
required: true, required: true,
rules: FormRules.codes, rules: FormRules.codes,
props: { props: {
'autocomplete': true, autocomplete: true,
countdown: false, countdown: false,
second: 59, second: 59,
aliasKey: 'mobile' aliasKey: 'mobile'
@@ -261,17 +290,21 @@ const FormRef = shallowRef<any>(null);
const tabPanel = ref<'verifyCode' | 'password'>('password'); const tabPanel = ref<'verifyCode' | 'password'>('password');
const formType = ref<'login' | 'register' | 'forget'>(props.defaultType); 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') { if (formType.value === 'register') {
return { return {
dict: RegisterForm.map(item => { dict: RegisterForm.map((item) => {
return { return {
...item, ...item,
defaultValue: item.key === 'mobile' defaultValue:
? cacheForm.mobile item.key === 'mobile' ? cacheForm.mobile : item.key === 'verificationcode' ? cacheForm.verificationcode : ''
: item.key === 'verificationcode'
? cacheForm.verificationcode
: ''
}; };
}), }),
showLabel: true, showLabel: true,
@@ -291,7 +324,7 @@ const FormConfig = computed<{ dict: FormListProps[], showLabel: boolean, layout:
} }
if (tabPanel.value === 'verifyCode') { if (tabPanel.value === 'verifyCode') {
return { return {
dict: VerifyForm.map(item => { dict: VerifyForm.map((item) => {
return { return {
...item, ...item,
defaultValue: item.key === 'mobile' ? cacheForm.mobile : '' defaultValue: item.key === 'mobile' ? cacheForm.mobile : ''
@@ -299,7 +332,7 @@ const FormConfig = computed<{ dict: FormListProps[], showLabel: boolean, layout:
}), }),
showLabel: false, showLabel: false,
layout: 'vertical', layout: 'vertical',
title: '欢迎登录你的GitCode账号', title: '欢迎登录你的GitCode账号1',
showBack: false showBack: false
}; };
} else { } else {
@@ -322,7 +355,7 @@ const catchFormErrors = (conf: { errors: Record<string, any[]> }) => {
} }
}; };
const handleResetPassword = async() => { const handleResetPassword = async () => {
if (FormRef.value) { if (FormRef.value) {
const formData = await FormRef.value.ValidateForm(); const formData = await FormRef.value.ValidateForm();
if (formData.type === 'success') { if (formData.type === 'success') {
@@ -347,7 +380,7 @@ const handleResetPassword = async() => {
} }
}; };
const handleConfirm = async() => { const handleConfirm = async () => {
let agreementFlag = false; let agreementFlag = false;
if (formType.value === 'forget') { if (formType.value === 'forget') {
handleResetPassword(); handleResetPassword();
@@ -397,10 +430,13 @@ const handleConfirm = async() => {
extraErrors.requestInfo = result.error.error_message; extraErrors.requestInfo = result.error.error_message;
} }
} else { } else {
const data = tabPanel.value === 'password' ? res : { const data =
mobile: res.mobile, tabPanel.value === 'password'
code: res.code ? res
}; : {
mobile: res.mobile,
code: res.code
};
const result = tabPanel.value === 'password' ? await toLogin(res) : await loginByMobile(data); const result = tabPanel.value === 'password' ? await toLogin(res) : await loginByMobile(data);
if (!result.error) { if (!result.error) {
const { user_id, mask, mobile } = result.data.data; const { user_id, mask, mobile } = result.data.data;
@@ -425,7 +461,7 @@ const handleConfirm = async() => {
/** /**
* 绑定华为云 * 绑定华为云
* @deprecated at 精简单注册登录 * @deprecated at 精简单注册登录
*/ */
// const handleBind = (res: any) => { // const handleBind = (res: any) => {
// if (!res.error) { // if (!res.error) {
// emits('login', res.data.data); // emits('login', res.data.data);
@@ -440,8 +476,10 @@ const handleClose = (close: Function) => {
}; };
/** 外链url */ /** 外链url */
const links = { 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', agreement:
'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' '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%81%E6%94%BF%E7%AD%96.md'
}; };
const links_hw = { 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]); const formData = await FormRef.value.ValidateFormKeys([conf.key]);
if (formData.type === 'success') { if (formData.type === 'success') {
clearFormError(conf.key); clearFormError(conf.key);
if (tabPanel.value === 'verifyCode' || formType.value === 'register' || formType.value === 'forget') { if (tabPanel.value === 'verifyCode' || formType.value === 'register' || formType.value === 'forget') {
const res = formType.value === 'login' const res =
? await getQuickLoginMsg({ mobile: conf.value }) formType.value === 'login'
: formType.value === 'forget' ? await getQuickLoginMsg({ mobile: conf.value })
: formType.value === 'forget'
? await getMobileEmailCode(conf.value) ? await getMobileEmailCode(conf.value)
: await verifyRegisterCode({ mobile: conf.value, type: 'REGISTER' }); : await verifyRegisterCode({ mobile: conf.value, type: 'REGISTER' });
if (!res.error) { if (!res.error) {
const source = formType.value === 'register' const source =
? RegisterForm formType.value === 'register' ? RegisterForm : formType.value === 'forget' ? ForgetPasswordForm : VerifyForm;
: formType.value === 'forget'
? ForgetPasswordForm
: VerifyForm;
cacheForm.mobile = conf.value; cacheForm.mobile = conf.value;
if (Number(res?.data.data.type === 1)) { if (Number(res?.data.data.type === 1)) {
isRegister.value = true; isRegister.value = true;
@@ -510,7 +546,7 @@ const handleCountDown = async(conf: { key: string, value: any, sourceKey: string
const message = isMobileEmail(conf.value); const message = isMobileEmail(conf.value);
dealLog(`验证码已发送到你的${message}, 请注意查收`, 'success'); dealLog(`验证码已发送到你的${message}, 请注意查收`, 'success');
} }
source.forEach(item => { source.forEach((item) => {
if (item.key === conf.sourceKey) { if (item.key === conf.sourceKey) {
if (item.props) { if (item.props) {
item.props.countdown = true; item.props.countdown = true;
@@ -564,15 +600,16 @@ const toCountDown = () => {
}; };
/** 刷新倒计时的状态 */ /** 刷新倒计时的状态 */
const refreshCountDown = (tab: string, menu: string) => { const refreshCountDown = (tab: string, menu: string) => {
const source = menu === 'register' const source =
? RegisterForm menu === 'register'
: formType.value === 'forget' ? RegisterForm
: formType.value === 'forget'
? ForgetPasswordForm ? ForgetPasswordForm
: tab === 'verifyCode' : tab === 'verifyCode'
? VerifyForm ? VerifyForm
: null; : null;
if (source) { 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 (countdown?.props && Object.prototype.hasOwnProperty.call(countdown.props, 'countdown')) {
if (cacheForm.countdownSecond !== 59 && menu === 'register' && isRegister.value) { if (cacheForm.countdownSecond !== 59 && menu === 'register' && isRegister.value) {
countdown.props.second = cacheForm.countdownSecond; countdown.props.second = cacheForm.countdownSecond;
@@ -584,7 +621,7 @@ const refreshCountDown = (tab: string, menu: string) => {
} }
}; };
/** 表单字段change事件 */ /** 表单字段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; const { key, errors } = conf;
if (errors) { if (errors) {
setFormErrorKey({ errors }); setFormErrorKey({ errors });
@@ -595,9 +632,9 @@ const handleFormChange = (conf: { key: string, errors: Record<string, any> | nul
const keys = Object.keys(formErrors); const keys = Object.keys(formErrors);
const currentForm = FormConfig.value.dict; const currentForm = FormConfig.value.dict;
if (currentForm?.length) { 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) { 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) { if (filterKeys.length) {
FormRef.value?.ClearFormFields(filterKeys); FormRef.value?.ClearFormFields(filterKeys);
} else { } else {
@@ -617,9 +654,9 @@ const errorMsg = computed(() => {
const keys = Object.keys(formErrors); const keys = Object.keys(formErrors);
const currentForm = FormConfig.value.dict; const currentForm = FormConfig.value.dict;
if (currentForm?.length) { 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) { 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); filterKeys.length && FormRef.value?.ClearFormFields(filterKeys);
hackMsgError(obj?.key === 'verificationcode' || obj?.key === 'code'); hackMsgError(obj?.key === 'verificationcode' || obj?.key === 'code');
} }
@@ -644,25 +681,28 @@ const hackMsgError = (show_error: boolean) => {
} }
}; };
watch(() => [tabPanel.value, formType.value], (newVal, oldVal) => { watch(
if (FormRef.value) { () => [tabPanel.value, formType.value],
FormRef.value.ClearForm(); (newVal, oldVal) => {
clearFormError(); if (FormRef.value) {
disabled.value = true; FormRef.value.ClearForm();
} clearFormError();
const [tab, menu] = newVal; disabled.value = true;
const [_, prev_menu] = oldVal;
if (menu !== 'register') {
if (interval) {
clearInterval(interval);
interval = null;
} }
const [tab, menu] = newVal;
const [_, prev_menu] = oldVal;
if (menu !== 'register') {
if (interval) {
clearInterval(interval);
interval = null;
}
}
if (prev_menu === 'register') {
isRegister.value = false;
}
refreshCountDown(tab, menu);
} }
if (prev_menu === 'register') { );
isRegister.value = false;
}
refreshCountDown(tab, menu);
});
// 移除参数 // 移除参数
localStorage.removeItem('loginReturnUrl'); localStorage.removeItem('loginReturnUrl');
@@ -684,7 +724,9 @@ const handleAuthLogin = (type: 'csdn' | 'gitee' | 'github') => {
// 保存注册上报类型和ref // 保存注册上报类型和ref
sessionStorage.setItem('loginType', props.triggerType || ''); sessionStorage.setItem('loginType', props.triggerType || '');
// window.open('http://localhost:5173/oauth/callback', '_blank', 'width=800, height=800, left=200, top=200'); // 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'); window.open(url, '_blank', 'width=800, height=800, left=400, top=200');
} }
}; };
@@ -707,6 +749,27 @@ onBeforeUnmount(() => {
clearInterval(interval); 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> </script>
<template> <template>
@@ -720,95 +783,8 @@ onBeforeUnmount(() => {
:append-to-body="appendToBody" :append-to-body="appendToBody"
:class="['login-modal', formType === 'login' ? 'login-modal-l' : 'login-modal-r']" :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> <template #default>
<div class="flex items-center justify-center mb-[24px] mt-[-2px]" v-if="formType === 'login'"> <iframe src="https://test.gitcode.net/login/?be_nested=1"></iframe>
<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>
</template> </template>
</Modal> </Modal>
</template> </template>
@@ -818,6 +794,11 @@ $active-title: var(--color-CG900);
$active-color: var(--color-CG800); $active-color: var(--color-CG800);
$inactive-title: var(--color-CG500); $inactive-title: var(--color-CG500);
$active-panel: var(--color-G800); $active-panel: var(--color-G800);
iframe {
border: 0;
width: 100%;
height: 60vh;
}
.login-modal { .login-modal {
box-sizing: border-box; box-sizing: border-box;
padding: 24px 28px 24px; padding: 24px 28px 24px;
@@ -869,7 +850,7 @@ $active-panel: var(--color-G800);
&::after { &::after {
position: absolute; position: absolute;
content: ''; content: '';
top:36px; top: 36px;
border-radius: 2px; border-radius: 2px;
border-bottom: 2px solid $active-panel; border-bottom: 2px solid $active-panel;
left: -4px; left: -4px;
@@ -893,7 +874,7 @@ $active-panel: var(--color-G800);
height: 20px; height: 20px;
} }
&-left { &-left {
color: #F2050D; color: #f2050d;
max-width: 240px; max-width: 240px;
min-width: 240px; min-width: 240px;
overflow: hidden; overflow: hidden;
@@ -994,7 +975,8 @@ $active-panel: var(--color-G800);
animation: shaking 0.25s ease-in-out; animation: shaking 0.25s ease-in-out;
} }
@keyframes shaking { @keyframes shaking {
0%, 100% { 0%,
100% {
transform: translateX(0); transform: translateX(0);
} }
25% { 25% {

View File

@@ -59,29 +59,29 @@ export const useAccountStore = defineStore('accountInfo', () => {
} }
}; };
// 可能有从博客跳转过来的,通过接口判断是否登陆 // 可能有从博客跳转过来的,通过接口判断是否登陆
const checkIsLogin = async () => { const checkIsLogin = async (access_token: string, refresh_token: string) => {
const { data, error } = await getUserToken(); // const { data, error } = await getUserToken();
if (data?.data) { // if (data?.data) {
const { access_token, refresh_token } = data.data; // const { access_token, refresh_token } = data.data;
localStorage.setItem('access_token', access_token); localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token); localStorage.setItem('refresh_token', refresh_token);
saveStatus(true); saveStatus(true);
const userRes = await getUserInfo(); const userRes = await getUserInfo();
if (userRes.data?.data) { if (userRes.data?.data) {
localStorage.setItem('userInfo', JSON.stringify(userRes.data?.data)); localStorage.setItem('userInfo', JSON.stringify(userRes.data?.data));
saveAccountInfo({ saveAccountInfo({
access_token: localStorage.getItem('access_token') || '', access_token: localStorage.getItem('access_token') || '',
refresh_token: localStorage.getItem('refresh_token') || '', refresh_token: localStorage.getItem('refresh_token') || '',
...userRes.data?.data ...userRes.data?.data
}); });
} // }
return true; return true;
} }
localStorage.removeItem('access_token'); // localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token'); // localStorage.removeItem('refresh_token');
localStorage.removeItem('userInfo'); // localStorage.removeItem('userInfo');
saveAccountInfo({}); // saveAccountInfo({});
return false; // return false;
}; };
const saveAtomgitUserName = (username: string) => { const saveAtomgitUserName = (username: string) => {
accountInfo.atomgit_username = username; accountInfo.atomgit_username = username;

View File

@@ -0,0 +1,51 @@
<template>
<div class="version-info">
<Card simple class="jyh-card mb-4">
<div class="card-title">基本信息</div>
<d-row class="mb-3">
<d-col :span="8">
<span class="card-key width-90">社区名称</span>
<span class="card-val">开发者社区</span>
</d-col>
<d-col :span="8">
<span class="card-key width-90">官网地址</span>
<span class="card-val">https://www.huaweicloud.com</span>
</d-col>
<d-col :span="8">
<span class="card-key width-90">联系方式</span>
<span class="card-val">950808-1</span>
</d-col>
</d-row>
<d-row>
<d-col :span="24">
<div class="flex">
<span class="card-key width-90 flex-shrink-0">社区描述</span>
<span class="card-val flex-grow-1"
>华为开发者社区致力于打造华为开发者专属的官方技术交流平台帮助开发者探索开发实践交流心得经验获悉业界动态解决开发问题汇聚华为云社区华为云领域博
问答视频等精彩内容形成开发者和技术爱好者交流与分享主阵地</span
>
</div>
</d-col>
</d-row>
</Card>
<Card simple class="jyh-card mb-4">
<div class="card-title">贡献者分布</div>
<p class="card-desc">有关Pull Request创建者的公司信息和地区分布(使用公共GitHub信息进行分析)</p>
<d-row class="mt-3">
<d-col :span="12">
<p>国家/地球分布</p>
</d-col>
<d-col :span="12">
<p>组织/公司分布</p>
</d-col>
</d-row>
</Card>
</div>
</template>
<style scoped lang="scss">
.card-desc {
font-size: 12px;
color: #808080;
}
</style>

View File

@@ -27,11 +27,13 @@
<d-tab id="bbxx" title="版本信息"></d-tab> <d-tab id="bbxx" title="版本信息"></d-tab>
<d-tab id="ylgx" title="依赖关系"></d-tab> <d-tab id="ylgx" title="依赖关系"></d-tab>
<d-tab id="ldxx" title="漏洞信息"></d-tab> <d-tab id="ldxx" title="漏洞信息"></d-tab>
<d-tab id="communityInfo" title="开发者社区信息"></d-tab>
</d-tabs> </d-tabs>
</div> </div>
<div class="version-detail-content"> <div class="version-detail-content">
<version-info v-if="selectTab === 'bbxx'"></version-info> <version-info v-if="selectTab === 'bbxx'"></version-info>
<vulnerabilities-list v-else-if="selectTab === 'ldxx'"></vulnerabilities-list> <vulnerabilities-list v-else-if="selectTab === 'ldxx'"></vulnerabilities-list>
<community-info v-else-if="selectTab === 'communityInfo'"></community-info>
</div> </div>
</div> </div>
</template> </template>
@@ -41,6 +43,7 @@ import { ref, reactive } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { getOrgInfo } from '@/views/Org/hooks/orgInfo'; import { getOrgInfo } from '@/views/Org/hooks/orgInfo';
import VulnerabilitiesList from './VulnerabilitiesList.vue'; import VulnerabilitiesList from './VulnerabilitiesList.vue';
import CommunityInfo from './CommunityInfo.vue';
import VersionInfo from './VersionInfo.vue'; import VersionInfo from './VersionInfo.vue';
const { namespace } = getOrgInfo(); const { namespace } = getOrgInfo();
const avatarUrl = '/src/assets/imgs/jyh/工程首字母图标.png'; const avatarUrl = '/src/assets/imgs/jyh/工程首字母图标.png';