Kelvin 3 years ago
parent 117b20430c
commit 3525532a8d

@ -0,0 +1,2 @@
{
}

@ -40,7 +40,7 @@ axios.interceptors.response.use(
(response: AxiosResponse<HttpResponse>) => { (response: AxiosResponse<HttpResponse>) => {
const res = response.data; const res = response.data;
// if the custom code is not 20000, it is judged as an error. // if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) { if (res.code !== 200) {
Message.error({ Message.error({
content: res.msg || 'Error', content: res.msg || 'Error',
duration: 5 * 1000, duration: 5 * 1000,

@ -9,21 +9,51 @@ export interface LoginData {
uuid: string; uuid: string;
} }
export interface RegisterData {
username: string;
password: string;
email: string;
code: string;
uuid: string;
inviteCode: string;
}
export interface LoginRes { export interface LoginRes {
token: string; token: string;
} }
export interface CaptchaData {
captchaEnabled: boolean;
img: string;
uuid: string;
}
export function login(data: LoginData) { export function login(data: LoginData) {
return axios.post<LoginRes>('/login', data); return axios.post<LoginRes>('/login', data);
} }
export function register(data: RegisterData) {
return axios.post<LoginRes>('/register', data);
}
export function logout() { export function logout() {
return axios.post<LoginRes>('/api/user/logout'); return axios.post<LoginRes>('/logout');
} }
export function getUserInfo() { export function getUserInfo() {
return axios.post<UserState>('/api/user/info'); return axios.get<UserState>('/getInfo');
} }
export function getMenuList() { export function getMenuList() {
return axios.post<RouteRecordNormalized[]>('/api/user/menu'); return axios.post<RouteRecordNormalized[]>('/api/user/menu');
} }
// 获取验证码
export function getCodeImg() {
return axios.get<CaptchaData>('/captchaImage2');
}
// 获取邮箱验证码
export function getEmailCode(email: string) {
return axios.get<CaptchaData>(`/captchaMail?email=${email}`);
}

@ -23,7 +23,7 @@
<Menu v-if="topMenu" /> <Menu v-if="topMenu" />
</div> </div>
<ul class="right-side"> <ul class="right-side">
<li> <!-- <li>
<a-tooltip :content="$t('settings.search')"> <a-tooltip :content="$t('settings.search')">
<a-button class="nav-btn" type="outline" :shape="'circle'"> <a-button class="nav-btn" type="outline" :shape="'circle'">
<template #icon> <template #icon>
@ -31,8 +31,8 @@
</template> </template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</li> </li> -->
<li> <!-- <li>
<a-tooltip :content="$t('settings.language')"> <a-tooltip :content="$t('settings.language')">
<a-button <a-button
class="nav-btn" class="nav-btn"
@ -60,7 +60,7 @@
</a-doption> </a-doption>
</template> </template>
</a-dropdown> </a-dropdown>
</li> </li> -->
<li> <li>
<a-tooltip <a-tooltip
:content=" :content="
@ -82,7 +82,7 @@
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</li> </li>
<li> <!-- <li>
<a-tooltip :content="$t('settings.navbar.alerts')"> <a-tooltip :content="$t('settings.navbar.alerts')">
<div class="message-box-trigger"> <div class="message-box-trigger">
<a-badge :count="9" dot> <a-badge :count="9" dot>
@ -108,7 +108,7 @@
<message-box /> <message-box />
</template> </template>
</a-popover> </a-popover>
</li> </li> -->
<li> <li>
<a-tooltip <a-tooltip
:content=" :content="
@ -150,10 +150,10 @@
:size="32" :size="32"
:style="{ marginRight: '8px', cursor: 'pointer' }" :style="{ marginRight: '8px', cursor: 'pointer' }"
> >
<img alt="avatar" :src="avatar" /> <img alt="avatar" src="//lf1-xgcdn-tos.pstatp.com/obj/vcloud/vadmin/start.8e0e4855ee346a46ccff8ff3e24db27b.png" />
</a-avatar> </a-avatar>
<template #content> <template #content>
<a-doption> <!-- <a-doption>
<a-space @click="switchRoles"> <a-space @click="switchRoles">
<icon-tag /> <icon-tag />
<span> <span>
@ -168,7 +168,7 @@
{{ $t('messageBox.userCenter') }} {{ $t('messageBox.userCenter') }}
</span> </span>
</a-space> </a-space>
</a-doption> </a-doption> -->
<a-doption> <a-doption>
<a-space @click="$router.push({ name: 'Setting' })"> <a-space @click="$router.push({ name: 'Setting' })">
<icon-settings /> <icon-settings />
@ -201,7 +201,7 @@
import useLocale from '@/hooks/locale'; import useLocale from '@/hooks/locale';
import useUser from '@/hooks/user'; import useUser from '@/hooks/user';
import Menu from '@/components/menu/index.vue'; import Menu from '@/components/menu/index.vue';
import MessageBox from '../message-box/index.vue'; // import MessageBox from '../message-box/index.vue';
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();

@ -44,6 +44,8 @@ export default {
'menu.faq': '常见问题', 'menu.faq': '常见问题',
'navbar.docs': '文档中心', 'navbar.docs': '文档中心',
'navbar.action.locale': '切换为中文', 'navbar.action.locale': '切换为中文',
'menu.pay': '帐户充值 ',
...localeSettings, ...localeSettings,
...localeMessageBox, ...localeMessageBox,
...localeLogin, ...localeLogin,

@ -1,10 +0,0 @@
export default {
path: 'https://arco.design',
name: 'arcoWebsite',
meta: {
locale: 'menu.arcoWebsite',
icon: 'icon-link',
requiresAuth: true,
order: 8,
},
};

@ -30,9 +30,27 @@ const DASHBOARD: AppRouteRecordRaw = {
meta: { meta: {
locale: 'menu.dashboard.monitor', locale: 'menu.dashboard.monitor',
requiresAuth: true, requiresAuth: true,
roles: ['admin'], roles: ['*'],
}, },
}, },
{
path: 'main',
name: 'Main',
component: () => import('@/views/api2gpt/main/index.vue'),
meta: {
locale: 'menu.dashboard.workplace',
requiresAuth: true,
}
},
{
path: 'pay',
name: 'Pay',
component: () => import('@/views/api2gpt/pay/index.vue'),
meta: {
locale: 'menu.pay',
requiresAuth: true,
},
}
], ],
}; };

@ -1,19 +1,11 @@
export type RoleType = '' | '*' | 'admin' | 'user'; export type RoleType = '' | '*' | 'admin' | 'user';
export interface UserState { export interface UserState {
name?: string; userId: number;
avatar?: string; userName: string;
job?: string; nickName: string;
organization?: string; email: string;
location?: string; phonenumber: string;
email?: string; sex: string;
introduction?: string; avatar: string;
personalWebsite?: string; password: string;
jobName?: string;
organizationName?: string;
locationName?: string;
phone?: string;
registrationDate?: string;
accountId?: string;
certification?: number;
role: RoleType;
} }

@ -22,4 +22,14 @@ export const regexUrl = new RegExp(
'i' 'i'
); );
// 定义获取 cookie 的函数
export function getCookie(name: string) {
const regexp = new RegExp(`(^| )${name}=([^;]*)(;|$)`)
const result = document.cookie.match(regexp)
if (result) {
return result[2]
}
return ''
}
export default null; export default null;

@ -0,0 +1,61 @@
<template>
<div class="container">
Main1111
</div>
</template>
<script lang="ts" setup>
</script>
<script lang="ts">
export default {
name: 'Main2',
};
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
.content {
display: flex;
margin-top: 12px;
&-left {
flex: 1;
margin-right: 16px;
overflow: hidden;
// background-color: var(--color-bg-2);
:deep(.arco-tabs-nav-tab) {
margin-left: 16px;
}
}
&-right {
width: 332px;
}
.tab-pane-wrapper {
padding: 0 16px 16px 16px;
}
}
</style>
<style lang="less" scoped>
.mobile {
.content {
display: block;
&-left {
margin-right: 0;
margin-bottom: 16px;
}
&-right {
width: 100%;
}
}
}
</style>

@ -0,0 +1,61 @@
<template>
<div class="container">
pay
</div>
</template>
<script lang="ts" setup>
</script>
<script lang="ts">
export default {
name: 'Pay',
};
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
.content {
display: flex;
margin-top: 12px;
&-left {
flex: 1;
margin-right: 16px;
overflow: hidden;
// background-color: var(--color-bg-2);
:deep(.arco-tabs-nav-tab) {
margin-left: 16px;
}
}
&-right {
width: 332px;
}
.tab-pane-wrapper {
padding: 0 16px 16px 16px;
}
}
</style>
<style lang="less" scoped>
.mobile {
.content {
display: block;
&-left {
margin-right: 0;
margin-bottom: 16px;
}
&-right {
width: 100%;
}
}
}
</style>

@ -16,7 +16,7 @@
const userStore = useUserStore(); const userStore = useUserStore();
const userInfo = computed(() => { const userInfo = computed(() => {
return { return {
name: userStore.name, name: userStore.userName,
}; };
}); });
</script> </script>

@ -41,21 +41,42 @@
</template> </template>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<a-form-item
field="code"
:rules="[{ required: true, message: $t('login.form.code.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
v-if="captchaEnabled"
>
<a-input
v-model="userInfo.code"
:placeholder="$t('login.form.code.placeholder')"
allow-clear
>
<template #prefix>
<icon-dice />
</template>
<template #append>
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
</template>
</a-input>
</a-form-item>
<a-space :size="16" direction="vertical"> <a-space :size="16" direction="vertical">
<div class="login-form-password-actions"> <div class="login-form-password-actions">
<a-checkbox <a-checkbox
checked="rememberPassword" checked="rememberPassword"
:model-value="loginConfig.rememberPassword" :model-value="loginConfig.rememberPassword"
@change="setRememberPassword as any" @change="setRememberPassword as any"
v-if="false"
> >
{{ $t('login.form.rememberPassword') }} {{ $t('login.form.rememberPassword') }}
</a-checkbox> </a-checkbox>
<a-link>{{ $t('login.form.forgetPassword') }}</a-link> <a-link v-if="false">{{ $t('login.form.forgetPassword') }}</a-link>
</div> </div>
<a-button type="primary" html-type="submit" long :loading="loading"> <a-button type="primary" html-type="submit" long :loading="loading">
{{ $t('login.form.login') }} {{ $t('login.form.login') }}
</a-button> </a-button>
<a-button type="text" long class="login-form-register-btn"> <a-button type="text" long class="login-form-register-btn" href="/login?type=register">
{{ $t('login.form.register') }} {{ $t('login.form.register') }}
</a-button> </a-button>
</a-space> </a-space>
@ -73,25 +94,40 @@
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import type { LoginData } from '@/api/user'; import type { LoginData } from '@/api/user';
import { getCodeImg } from '@/api/user';
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const errorMessage = ref(''); const errorMessage = ref('');
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const userStore = useUserStore(); const userStore = useUserStore();
const codeUrl = ref("");
//
const captchaEnabled = ref(true);
const loginConfig = useStorage('login-config', { const loginConfig = useStorage('login-config', {
rememberPassword: true, rememberPassword: false,
username: 'admin', // username: '',
password: 'admin', // demo default value password: '',
code: '',
uuid: ''
}); });
const userInfo = reactive({ const userInfo = reactive({
username: loginConfig.value.username, username: loginConfig.value.username,
password: loginConfig.value.password, password: loginConfig.value.password,
code: '111', code: '',
uuid: '222' uuid: ''
}); });
const getCode = async () => {
const res = await getCodeImg();
captchaEnabled.value = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = `data:image/gif;base64,${res.data.img}`;
userInfo.uuid = res.data.uuid;
}
};
const handleSubmit = async ({ const handleSubmit = async ({
errors, errors,
values, values,
@ -114,12 +150,13 @@
Message.success(t('login.form.login.success')); Message.success(t('login.form.login.success'));
const { rememberPassword } = loginConfig.value; const { rememberPassword } = loginConfig.value;
const { username, password } = values; const { username, password } = values;
//
// The actual production environment requires encrypted storage.
loginConfig.value.username = rememberPassword ? username : ''; loginConfig.value.username = rememberPassword ? username : '';
loginConfig.value.password = rememberPassword ? password : ''; loginConfig.value.password = rememberPassword ? password : '';
} catch (err) { } catch (err) {
errorMessage.value = (err as Error).message; errorMessage.value = (err as Error).message;
if (captchaEnabled.value) {
getCode();
}
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -128,9 +165,16 @@
const setRememberPassword = (value: boolean) => { const setRememberPassword = (value: boolean) => {
loginConfig.value.rememberPassword = value; loginConfig.value.rememberPassword = value;
}; };
getCode();
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.login-code-img {
height: 40px;
padding-left: 12px;
}
.login-form { .login-form {
&-wrapper { &-wrapper {
width: 320px; width: 320px;

@ -0,0 +1,216 @@
<template>
<div class="login-form-wrapper">
<div class="login-form-title">{{ $t('register.form.title') }}</div>
<div class="login-form-sub-title">{{ $t('register.form.title') }}</div>
<div class="login-form-error-msg">{{ errorMessage }}</div>
<a-form
ref="registerForm"
:model="userInfo"
class="login-form"
layout="vertical"
@submit="handleSubmit"
>
<a-form-item
field="username"
:rules="[{ required: true, message: $t('register.form.userName.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input
v-model="userInfo.username"
:placeholder="$t('register.form.userName.placeholder')"
>
<template #prefix>
<icon-user />
</template>
</a-input>
</a-form-item>
<a-form-item
field="password"
:rules="[{ required: true, message: $t('register.form.password.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input-password
v-model="userInfo.password"
:placeholder="$t('register.form.password.placeholder')"
allow-clear
>
<template #prefix>
<icon-lock />
</template>
</a-input-password>
</a-form-item>
<a-form-item
field="confirmPassword"
:rules="[{ required: true, message: $t('register.form.confirmPassword.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input-password
v-model="userInfo.confirmPassword"
:placeholder="$t('register.form.confirmPassword.placeholder')"
allow-clear
>
<template #prefix>
<icon-lock />
</template>
</a-input-password>
</a-form-item>
<a-form-item
field="email"
:rules="[{ required: true, message: $t('register.form.email.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input
v-model="userInfo.email"
:placeholder="$t('register.form.email.placeholder')"
>
<template #prefix>
<icon-email />
</template>
</a-input>
</a-form-item>
<a-form-item
field="code"
:rules="[{ required: true, message: $t('register.form.code.errMsg') }]"
:validate-trigger="['change', 'blur']"
hide-label
>
<a-input
v-model="userInfo.code"
:placeholder="$t('register.form.code.placeholder')"
>
<template #prefix>
<icon-dice />
</template>
<template #append>
<a-button @click="getCode"></a-button>
</template>
</a-input>
</a-form-item>
<a-space :size="16" direction="vertical">
<a-button type="primary" html-type="submit" long :loading="loading">
{{ $t('login.form.register') }}
</a-button>
<a-button type="text" long class="login-form-register-btn" href="/login">
{{ $t('login.form.login') }}
</a-button>
</a-space>
</a-form>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading';
import type { RegisterData } from '@/api/user';
import { getEmailCode, register } from '@/api/user';
import { getCookie } from '@/utils/index';
const router = useRouter();
const { t } = useI18n();
const errorMessage = ref('');
const { loading, setLoading } = useLoading();
const inviteCode = getCookie('inviteCode')
const userInfo = reactive({
username: '',
password: '',
confirmPassword: '',
email: '',
code: '',
uuid: ''
});
const getCode = async () => {
const emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/;
if(userInfo.email === '' || !emailRegex.test(userInfo.email)){
Message.error(t('register.form.email.error'));
} else {
const res = await getEmailCode(userInfo.email);
userInfo.uuid = res.data.uuid;
}
};
const handleSubmit = async ({
errors,
values,
}: {
errors: Record<string, ValidatedError> | undefined;
values: Record<string, any>;
}) => {
if (loading.value) return;
if (userInfo.password !== userInfo.confirmPassword) {
Message.error(t('register.form.password.nomatch.error'));
return;
}
if(userInfo.password.length < 6){
Message.error('密码长度不能小于6位');
return;
}
if (!errors) {
setLoading(true);
try {
values.inviteCode = inviteCode
const req = await register(values as RegisterData);
Message.success(`${userInfo.email} 注册成功`);
setTimeout(function(){
window.location.href = "/login";
}, 1500);
} catch (err) {
errorMessage.value = (err as Error).message;
} finally {
setLoading(false);
}
}
};
</script>
<style lang="less" scoped>
.login-code-img {
height: 40px;
padding-left: 12px;
}
.login-form {
&-wrapper {
width: 320px;
}
&-title {
color: var(--color-text-1);
font-weight: 500;
font-size: 24px;
line-height: 32px;
}
&-sub-title {
color: var(--color-text-3);
font-size: 16px;
line-height: 24px;
}
&-error-msg {
height: 32px;
color: rgb(var(--red-6));
line-height: 32px;
}
&-password-actions {
display: flex;
justify-content: space-between;
}
&-register-btn {
color: var(--color-text-3) !important;
}
}
</style>

@ -5,12 +5,13 @@
alt="logo" alt="logo"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image" src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image"
/> />
<div class="logo-text">Arco Design Pro</div> <div class="logo-text">Api2Gpt</div>
</div> </div>
<LoginBanner /> <LoginBanner />
<div class="content"> <div class="content">
<div class="content-inner"> <div class="content-inner">
<LoginForm /> <LoginForm v-if="type!='register'"/>
<RegisterForm v-if="type=='register'"/>
</div> </div>
<div class="footer"> <div class="footer">
<Footer /> <Footer />
@ -20,9 +21,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import { useRoute } from "vue-router";
import Footer from '@/components/footer/index.vue'; import Footer from '@/components/footer/index.vue';
import LoginBanner from './components/banner.vue'; import LoginBanner from './components/banner.vue';
import LoginForm from './components/login-form.vue'; import LoginForm from './components/login-form.vue';
import RegisterForm from './components/register-form.vue';
const type = ref("");
const route = useRoute(); //
if(route.query.type){
type.value = route.query.type as string;
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

@ -1,11 +1,13 @@
export default { export default {
'login.form.title': '登录 Arco Design Pro', 'login.form.title': '登录 Api2Gpt',
'login.form.userName.errMsg': '用户名不能为空', 'login.form.userName.errMsg': '用户名不能为空',
'login.form.password.errMsg': '密码不能为空', 'login.form.password.errMsg': '密码不能为空',
'login.form.login.errMsg': '登录出错,轻刷新重试', 'login.form.code.errMsg': '验证码不能为空',
'login.form.login.errMsg': '登录出错,请刷新重试',
'login.form.login.success': '欢迎使用', 'login.form.login.success': '欢迎使用',
'login.form.userName.placeholder': '用户名admin', 'login.form.userName.placeholder': '用户名:',
'login.form.password.placeholder': '密码admin', 'login.form.password.placeholder': '密码:',
'login.form.code.placeholder': '验证码:',
'login.form.rememberPassword': '记住密码', 'login.form.rememberPassword': '记住密码',
'login.form.forgetPassword': '忘记密码', 'login.form.forgetPassword': '忘记密码',
'login.form.login': '登录', 'login.form.login': '登录',
@ -16,4 +18,18 @@ export default {
'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有', 'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有',
'login.banner.slogan3': '接入可视化增强工具AUX', 'login.banner.slogan3': '接入可视化增强工具AUX',
'login.banner.subSlogan3': '实现灵活的区块式开发', 'login.banner.subSlogan3': '实现灵活的区块式开发',
'register.form.title': '注册 Api2Gpt',
'register.form.userName.errMsg': '用户名不能为空',
'register.form.password.errMsg': '密码不能为空',
'register.form.confirmPassword.errMsg': '确认密码不能为空',
'register.form.email.errMsg': '邮箱不能为空',
'register.form.code.errMsg': '验证码不能为空',
'register.form.userName.placeholder': '用户名:',
'register.form.password.placeholder': '密码:',
'register.form.confirmPassword.placeholder': '确认密码:',
'register.form.email.placeholder': '邮箱:',
'register.form.code.placeholder': '验证码:',
'register.form.email.error': '邮箱格式不正确',
'register.form.email.success': '邮箱验证码发送成功',
'register.form.password.nomatch.error': '两次输入密码不一致',
}; };

Loading…
Cancel
Save