add qq oauth2 support / start landing page

This commit is contained in:
landaiqing
2024-08-19 23:11:22 +08:00
parent 8998259791
commit 498807ca66
28 changed files with 1123 additions and 129 deletions

View File

@@ -16,4 +16,4 @@ VITE_TITLE_NAME='五味子云相册'
VITE_APP_TOKEN_KEY='Bearer'
# the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/socket'
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/gws'

View File

@@ -15,4 +15,4 @@ VITE_TITLE_NAME='五味子云相册'
VITE_APP_TOKEN_KEY='Bearer'
# the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/socket'
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/gws'

3
components.d.ts vendored
View File

@@ -7,6 +7,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
3DCard: typeof import('./src/components/3DCard/3DCard.vue')['default']
AButton: typeof import('ant-design-vue/es')['Button']
AButtonGroup: typeof import('ant-design-vue/es')['ButtonGroup']
ACard: typeof import('ant-design-vue/es')['Card']
@@ -27,7 +28,9 @@ declare module 'vue' {
ATimePicker: typeof import('ant-design-vue/es')['TimePicker']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
Card3D: typeof import('./src/components/3DCard/Card3D.vue')['default']
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
EffectsCard: typeof import('./src/components/EffectsCard/EffectsCard.vue')['default']
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']

View File

@@ -17,6 +17,7 @@ export default [
"@typescript-eslint/no-explicit-any": "off",
// "no-unused-vars": "off",
// "@typescript-eslint/no-unused-vars": ["off"],
"vue/multi-word-component-names":"off",
}
}
];

View File

@@ -1,7 +1,23 @@
<template>
<LayOut/>
<AConfigProvider
:locale="lang.lang === 'en' ? enUS : zhCN"
:theme="app.themeConfig"
>
<router-view v-slot="{ Component, route }">
<transition name="animation" mode="out-in">
<component :is="Component" :key="route.path"/>
</transition>
</router-view>
</AConfigProvider>
</template>
<script setup lang="ts">
import LayOut from "@/layout/default/DefaultLayout.vue";
import enUS from 'ant-design-vue/es/locale/en_US';
import zhCN from 'ant-design-vue/es/locale/zh_CN';
import useStore from "@/store/index.ts";
const app = useStore().theme;
const lang = useStore().lang;
</script>
<style scoped lang="scss">
</style>

View File

@@ -3,9 +3,12 @@ import {service} from "@/utils/alova/service.ts";
/**
* Get Github OAuth URL
*/
export const getGithubUrl = () => {
export const getGithubUrl = (state: string) => {
return service.Get('/api/oauth/github/get_url',
{
params: {
state: state
},
meta: {
ignoreToken: true,
},

18
src/api/oauth/qq.ts Normal file
View File

@@ -0,0 +1,18 @@
import {service} from "@/utils/alova/service.ts";
export const getQQUrl = (state: string) => {
return service.Get('/api/oauth/qq/get_url',
{
params: {
state: state
},
meta: {
ignoreToken: true,
},
cacheFor: {
mode: "restore",
expire: 1000 * 60 * 60 * 24 * 30 // 30 days
}
}
);
};

View File

@@ -4,7 +4,7 @@ import {service} from "@/utils/alova/service.ts";
* 生成客户端id
*/
export const generateClientId = () => {
return service.Get('/api/oauth/generate_client_id',
return service.Get('/api/oauth/wechat/generate_client_id',
{
meta: {
ignoreToken: true,
@@ -21,7 +21,7 @@ export const generateClientId = () => {
* @param clientId
*/
export const generateQrCode = (clientId: string) => {
return service.Get('/api/oauth/get_temp_qrcode',
return service.Get('/api/oauth/wechat/get_temp_qrcode',
{
params: {
client_id: clientId

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1 @@
<svg t="1724030510306" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1626" width="200" height="200"><path d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m259.2-569.6H480c-12.8 0-25.6 12.8-25.6 25.6v64c0 12.8 12.8 25.6 25.6 25.6h176c12.8 0 25.6 12.8 25.6 25.6V608c0 41.6-35.2 76.8-76.8 76.8h-240c-12.8 0-25.6-12.8-25.6-25.6V416c0-41.6 35.2-76.8 76.8-76.8h355.2c12.8 0 25.6-12.8 25.6-25.6v-64c0-12.8-12.8-25.6-25.6-25.6H416c-105.6 0-188.8 86.4-188.8 188.8V768c0 12.8 12.8 25.6 25.6 25.6h374.4c92.8 0 169.6-76.8 169.6-169.6V480c0-12.8-12.8-25.6-25.6-25.6z" fill="#888888" p-id="1627"></path></svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,241 @@
<template>
<div class="cards" style="transform: rotateX(-10.2deg) rotateY(-17.8deg);">
<h3>Movies</h3>
<h1>Popular</h1>
<div class="card card__one">
<div class="card__bg" style="background-position: -8.01px 4.59px;"></div>
<img class="card__img" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_mono.png"
style="transform: translateX(17.8px) translateY(-10.2px);">
<div class="card__text">
<p class="card__title">Princess Mononoke</p>
</div>
</div>
<div class="card card__two">
<div class="card__bg" style="background-position: -8.01px 4.59px;"></div>
<img class="card__img" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_chihiro.png"
style="transform: translateX(17.8px) translateY(-10.2px);">
<div class="card__text">
<p class="card__title">Spirited Away</p>
</div>
</div>
<div class="card card__three">
<div class="card__bg" style="background-position: -8.01px 4.59px;"></div>
<img class="card__img" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_howlcastle.png"
style="transform: translateX(17.8px) translateY(-10.2px);">
<div class="card__text">
<p class="card__title">Howl's Moving Castle</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted} from "vue";
onMounted(() => {
const cards: any = document.querySelector(".cards");
const images: any = document.querySelectorAll(".card__img");
const backgrounds: any = document.querySelectorAll(".card__bg");
const range = 40;
// const calcValue = (a, b) => (((a * 100) / b) * (range / 100) -(range / 2)).toFixed(1);
const calcValue = (a, b) => (a / b * range - range / 2).toFixed(1); // thanks @alice-mx
let timeout: any = null;
document.addEventListener('mousemove', ({x, y}) => {
if (timeout) {
window.cancelAnimationFrame(timeout);
}
timeout = window.requestAnimationFrame(() => {
const yValue: any = calcValue(y, window.innerHeight);
const xValue: any = calcValue(x, window.innerWidth);
cards.style.transform = `rotateX(${yValue}deg) rotateY(${xValue}deg)`;
[].forEach.call(images, (image: any) => {
image.style.transform = `translateX(${-xValue}px) translateY(${yValue}px)`;
});
[].forEach.call(backgrounds, (background: any) => {
background.style.backgroundPosition = `${xValue * .45}px ${-yValue * .45}px`;
});
});
}, false);
});
</script>
<style scoped>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,800");
html, body {
height: 100%;
}
body {
align-items: center;
background: #642B73;
background: linear-gradient(to bottom, #C6426E, #642B73);
display: flex;
font-family: "Open Sans", sans;
justify-content: center;
overflow: hidden;
perspective: 1800px;
text-align: center;
margin: 0 20px;
}
h1 {
color: #3e3e42;
font-size: 32px;
font-weight: 800;
letter-spacing: -1px;
margin-bottom: 30px;
transform: translateZ(35px);
}
h3 {
color: #eb285d;
font-size: 16px;
margin-bottom: 6px;
transform: translateZ(25px);
}
.cards {
background: #fff;
border-radius: 15px;
box-shadow: 0px 10px 20px 20px rgba(0, 0, 0, 0.17);
display: inline-block;
padding: 30px 35px;
perspective: 1800px;
text-align: left;
transform-origin: 50% 50%;
transform-style: preserve-3d;
transform: rotateX(11deg) rotateY(16.5deg);
min-width: 595px;
}
.card {
border-radius: 15px;
box-shadow: 5px 5px 20px -5px rgba(0, 0, 0, 0.6);
display: inline-block;
height: 250px;
overflow: hidden;
perspective: 1200px;
position: relative;
transform-style: preserve-3d;
transform: translatez(35px);
transition: transform 200ms ease-out;
width: 175px;
text-align: center;
}
.card:not(:last-child) {
margin-right: 30px;
}
.card__img {
position: relative;
height: 100%;
}
.card__bg {
bottom: -50px;
left: -50px;
position: absolute;
right: -50px;
top: -50px;
transform-origin: 50% 50%;
transform: translateZ(-50px);
z-index: 0;
}
.card__one .card__img {
top: 14px;
right: -10px;
height: 110%;
}
.card__one .card__bg {
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_monobg.jpg") center/cover no-repeat;
}
.card__two .card__img {
top: 25px;
}
.card__two .card__bg {
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_spirited.jpg") center/cover no-repeat;
}
.card__three .card__img {
top: 5px;
left: -4px;
height: 110%;
}
.card__three .card__bg {
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/62105/3dr_howlbg.jpg") center/cover no-repeat;
}
.card__text {
align-items: center;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.55) 100%);
bottom: 0;
display: flex;
flex-direction: column;
height: 70px;
justify-content: center;
position: absolute;
width: 100%;
z-index: 2;
}
.card__title {
color: #fff;
font-size: 18px;
font-weight: 700;
padding: 0 10px;
margin-bottom: 3px;
}
.notice {
background: gold;
border-top-left-radius: 6px;
bottom: 0;
font-family: monospace;
font-size: 14px;
padding: 8px 10px;
position: absolute;
right: -20px;
}
.twitter__link {
cursor: pointer;
position: absolute;
right: -10px;
top: 12px;
z-index: -1;
background: #00aced;
border-radius: 20px;
height: 30px;
text-decoration: none;
padding-right: 10px;
justify-content: space-between;
font-weight: 600;
display: flex;
align-items: center;
color: #fff;
font-size: 14px;
width: 74px;
opacity: 0.4;
}
.twitter__link:hover {
opacity: 1;
}
.twitter__icon {
height: 30px;
}
</style>

View File

@@ -0,0 +1,373 @@
<template>
<div class="center">
<h1>Daily UI 002: Credit Card Checkout</h1>
<div class="wrapper">
<button class="checkout">Checkout</button>
<div class="card-wrap">
<div class="card">
<!--?xml version="1.0" encoding="utf-8"?-->
<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 216.3 234.1" enable-background="new 0 0 216.3 234.1" xml:space="preserve">
<g id="shadow">
<g>
<path fill="#5A488F" d="M178.1,211.7c14.8-2.1,23.9-5.1,23.9-8.3c0-6.6-37.8-11.9-84.4-11.9c-46.6,0-84.4,5.3-84.4,11.9
c0,5.1,23,9.5,55.3,11.2c-10.5,2.1-16.7,4.7-16.7,7.6c0,6.6,32.4,11.9,72.3,11.9c39.9,0,72.3-5.3,72.3-11.9
C216.3,217.7,200.8,213.7,178.1,211.7z"></path>
</g>
</g>
<g id="back_hand">
<g>
<path fill="#FFFFFF" d="M43.4,127.7c-2.6-4.5-3.7-9-1.9-17.2c0.4-1.6-1-3.1-2.6-2.8c-6.9,1.2-6.2,14.1-6.2,14.1H4.8
c-2,0-3.6,1.6-3.6,3.6v0.1c0,2,1.6,3.6,3.6,3.6h10.2v20.5c0,0.5,0.4,1,1,1H18h4.5h8.8c7.5,0,14-5.6,14.7-13.1
C46.4,133.8,45.3,130.4,43.4,127.7z"></path>
<g>
<path fill="#DAEDF7" d="M32.7,125.9h-3.4c-2.3,0-4-1.9-3.8-4.2v0h7.2L32.7,125.9z"></path>
</g>
<g>
<path fill="#665AA7" d="M18,130.2H4.8c-2.6,0-4.8-2.1-4.8-4.8c0-2.7,2.1-4.8,4.8-4.8h27.9c0.6,0,1.1,0.5,1.1,1.1
s-0.5,1.1-1.1,1.1H4.8c-1.4,0-2.5,1.1-2.5,2.5c0,1.4,1.1,2.6,2.5,2.6H18c0.6,0,1.1,0.5,1.1,1.1S18.6,130.2,18,130.2z"></path>
</g>
<g>
<path fill="#DAEDF7" d="M29.3,130.9c8.3-0.6,11.2,16.6,2,19.1c-3.3,0.6-2.8-0.8-2.8-0.8l0.6-2.6c0,0-2.7-2.4,0-7.4
C27.9,136.2,29.3,133.6,29.3,130.9z"></path>
</g>
<g>
<path fill="#665AA7" d="M31.7,151.6H18c-0.6,0-1.1-0.5-1.1-1.1s0.5-1.1,1.1-1.1h13.7c7.3,0,13.3-5.9,13.3-13.3
c0-2.8-0.9-5.5-2.5-7.7c0,0,0-0.1-0.1-0.1c-2.6-4.4-4-9.2-2-18.1c0.1-0.4,0-0.8-0.3-1.1c-0.3-0.3-0.7-0.4-1-0.4
c-3.6,0.6-5.5,7-5.3,17.1c0,0.6-0.5,1.1-1.1,1.2c-0.6,0-1.1-0.5-1.2-1.1c-0.3-11.7,2.3-18.6,7.2-19.4c1.1-0.2,2.2,0.2,3,1
c0.8,0.8,1.1,2,0.9,3.2c-1.8,8.1-0.7,12.3,1.7,16.4c1.9,2.7,2.9,5.8,2.9,9C47.2,144.7,40.3,151.6,31.7,151.6z"></path>
</g>
<g>
<path fill="#665AA7" d="M26.1,137.3h-8.5c-2.4,0-4.3-1.9-4.3-4.3v-0.8c0-2.4,1.9-4.3,4.3-4.3h8.5c2.4,0,4.3,1.9,4.3,4.3v0.8
C30.4,135.4,28.5,137.3,26.1,137.3z M17.6,130.2c-1.1,0-2.1,0.9-2.1,2.1v0.8c0,1.1,0.9,2.1,2.1,2.1h8.5c1.1,0,2.1-0.9,2.1-2.1
v-0.8c0-1.1-0.9-2.1-2.1-2.1H17.6z"></path>
</g>
<g>
<path fill="#665AA7" d="M26.1,144.5h-8.5c-2.4,0-4.3-1.9-4.3-4.3v-0.8c0-2.4,1.9-4.3,4.3-4.3h8.5c2.4,0,4.3,1.9,4.3,4.3v0.8
C30.4,142.5,28.5,144.5,26.1,144.5z M17.6,137.3c-1.1,0-2.1,0.9-2.1,2.1v0.8c0,1.1,0.9,2.1,2.1,2.1h8.5c1.1,0,2.1-0.9,2.1-2.1
v-0.8c0-1.1-0.9-2.1-2.1-2.1H17.6z"></path>
</g>
<g>
<path fill="#665AA7" d="M26.1,151.6h-8.5c-2.4,0-4.3-1.9-4.3-4.3v-0.8c0-2.4,1.9-4.3,4.3-4.3h8.5c2.4,0,4.3,1.9,4.3,4.3v0.8
C30.4,149.7,28.5,151.6,26.1,151.6z M17.6,144.5c-1.1,0-2.1,0.9-2.1,2.1v0.8c0,1.1,0.9,2.1,2.1,2.1h8.5c1.1,0,2.1-0.9,2.1-2.1
v-0.8c0-1.1-0.9-2.1-2.1-2.1H17.6z"></path>
</g>
</g>
</g>
<g id="card">
<g>
<path fill="#E02E92" d="M178.4,203.4H56.8c-4.8,0-8.6-3.9-8.6-8.6V8.6c0-4.8,3.9-8.6,8.6-8.6h121.5c4.8,0,8.6,3.9,8.6,8.6v186.2
C187,199.6,183.1,203.4,178.4,203.4z"></path>
</g>
<g>
<rect x="144" fill="#363284" width="27.9" height="203.4"></rect>
</g>
</g>
<g id="front_hand">
<g>
<g>
<path fill="#FFFFFF" d="M132.2,131.7c-2.6-4.5-3.7-9-1.9-17.2c0.4-1.6-1-3.1-2.6-2.8c-6.9,1.1-6.2,14.1-6.2,14.1H93.6
c-2,0-3.6,1.6-3.6,3.6v0.1c0,2,1.6,3.6,3.6,3.6H104l0.4,21.4h16.2c7.9,0,14.4-6.4,14.4-14.4C135,137,134,134.1,132.2,131.7z"></path>
</g>
<g>
<path fill="#DAEDF7" d="M125,125.7c0,0-0.6-2.5,0.8-6.9c0.6-1.9-2-2.9-3.3-0.6c0,0-0.9,1.5-0.9,7.3
C124.5,125.7,125,125.7,125,125.7z"></path>
</g>
<g>
<path fill="#665AA7" d="M120.6,155.6h-13.7c-2.6,0-4.7-2.1-4.7-4.7c0-1.4,0.6-2.7,1.6-3.6c-1-0.9-1.6-2.1-1.6-3.5
c0-1.4,0.6-2.7,1.6-3.6c-1-0.9-1.6-2.1-1.6-3.5c0-0.9,0.3-1.8,0.7-2.5h-9.3c-2.6,0-4.8-2.1-4.8-4.8c0-2.7,2.1-4.8,4.8-4.8h26.7
c0-4.1,0.8-13,7.2-14.1c1.1-0.2,2.2,0.2,3,1c0.8,0.8,1.1,2,0.9,3.2c-1.8,8.1-0.7,12.3,1.7,16.4c1.9,2.7,2.9,5.8,2.9,9
C136.1,148.7,129.1,155.6,120.6,155.6z M108,147.3c0,0.6-0.5,1.1-1.1,1.1c-1.3,0-2.4,1.1-2.4,2.4c0,1.4,1.1,2.5,2.4,2.5h13.7
c7.3,0,13.3-5.9,13.3-13.3c0-2.8-0.9-5.5-2.5-7.7c0,0,0-0.1-0.1-0.1c-2.6-4.4-4-9.2-2-18.1c0.1-0.4,0-0.8-0.3-1.1
c-0.3-0.3-0.7-0.4-1-0.4c-4.9,0.8-5.3,9.6-5.3,11.9h1.6c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1H93.6c-1.4,0-2.5,1.1-2.5,2.5
c0,1.4,1.1,2.6,2.5,2.6h13.2c0.6,0,1.1,0.5,1.1,1.1s-0.5,1.1-1.1,1.1c-1.3,0-2.4,1.1-2.4,2.4c0,1.2,0.8,2.2,2,2.4
c0.5,0.1,0.9,0.6,0.9,1.1l0,0.1c0,0.5-0.4,1-0.9,1.1c-1.1,0.2-2,1.2-2,2.4c0,1.4,1.1,2.5,2.4,2.5
C107.5,146.2,108,146.7,108,147.3z"></path>
</g>
<g>
<path fill="#FFFFFF" d="M88.3,136"></path>
</g>
</g>
</g>
<g id="coins">
<g>
<g>
<g>
<g>
<path fill="#ED912F" d="M206.4,216c0,1.5-1,2.7-2.5,3c-5.6,1.1-20.3,3.2-38.2,3.2s-32.6-2-38.2-3.2c-1.4-0.3-2.5-1.6-2.5-3
v-4.8h81.4V216z"></path>
</g>
<g>
<ellipse fill="#F9C441" cx="165.8" cy="211.2" rx="40.7" ry="3.9"></ellipse>
</g>
</g>
<g>
<g>
<path fill="#ED912F" d="M206.4,207.8c0,1.5-1,2.7-2.5,3c-5.6,1.1-20.3,3.2-38.2,3.2s-32.6-2-38.2-3.2c-1.4-0.3-2.5-1.6-2.5-3
V203h81.4V207.8z"></path>
</g>
<g>
<ellipse fill="#F9C441" cx="165.8" cy="203" rx="40.7" ry="3.9"></ellipse>
</g>
</g>
<g>
<g>
<path fill="#ED912F" d="M213.7,200.5c0,1.5-1,2.7-2.5,3c-5.6,1.1-20.3,3.2-38.2,3.2s-32.6-2-38.2-3.2c-1.4-0.3-2.5-1.6-2.5-3
v-4.8h81.4V200.5z"></path>
</g>
<g>
<ellipse fill="#F9C441" cx="173" cy="195.7" rx="40.7" ry="3.9"></ellipse>
</g>
</g>
<g>
<g>
<path fill="#ED912F" d="M196.4,192.8c0,1.5-1,2.7-2.5,3c-5.6,1.1-20.3,3.2-38.2,3.2s-32.6-2-38.2-3.2c-1.4-0.3-2.5-1.6-2.5-3
V188h81.4V192.8z"></path>
</g>
<g>
<ellipse fill="#F9C441" cx="155.7" cy="188" rx="40.7" ry="3.9"></ellipse>
</g>
</g>
<g>
<defs>
<path id="SVGID_1_" d="M195.9,192.5V188c0-2.1-18.3-3.9-40.8-3.9s-40.8,1.7-40.8,3.9v4.8c0,1.5,1.3,2.7,2.8,3
c2.9,0.6,7.9,1.4,15.1,2v2.7c0,0.1,0.1,0.2,0.1,0.3c-4.6,0.6-7.2,1.4-7.2,2.2v4.8c0,1,0.5,1.9,1.3,2.5c-0.8,0.3-1.3,0.6-1.3,1
v4.8c0,1.5,0.9,2.7,2.3,3c5.6,1.1,20.3,3.2,38.1,3.2c17.9,0,32.3-2,38-3.2c1.4-0.3,2.2-1.6,2.2-3v-4.8c0-0.3-0.4-0.7-1.3-1
c0.8-0.6,1.3-1.5,1.3-2.5v-3.5c2.6-0.3,3.5-0.6,4.7-0.8c1.4-0.3,2.4-1.6,2.4-3v-4.8C212.7,194.4,205.6,193.2,195.9,192.5z"></path>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" overflow="visible"></use>
</clipPath>
</g>
</g>
</g>
<g>
<defs>
<path id="SVGID_3_" d="M196.4,192.5V188c0-2.1-18.2-3.9-40.7-3.9c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0c-5.6,0-11,0.1-15.8,0.3
c3.5-3.5,6.1-6.3,7.6-8.1c0.9-1.1,1-2.8,0.1-3.9l-3-3.8c-1.3-1.7-16.7,8.3-34.3,22.3c-17.6,14-30.8,26.7-29.5,28.3l3,3.8
c0.9,1.2,2.5,1.5,3.8,0.8c5.1-2.6,17.9-10.2,31.9-21.3c2.3-1.9,4.6-3.7,6.7-5.5c2,0.2,4.2,0.5,6.6,0.7v2.7c0,0.1,0.1,0.2,0.1,0.3
c-4.6,0.6-7.2,1.4-7.2,2.2v4.8c0,1,0.5,1.9,1.3,2.5c-0.8,0.3-1.3,0.6-1.3,1v4.8c0,1.5,0.9,2.7,2.3,3c5.6,1.1,20.3,3.2,38.1,3.2
c0.1,0,0.1,0,0.2,0c0.1,0,0.1,0,0.2,0c17.9,0,32.6-2,38.2-3.2c1.4-0.3,2.5-1.6,2.5-3v-4.8c0-0.3-0.4-0.7-1.3-1
c0.8-0.6,1.3-1.5,1.3-2.5v-3.5c1.9-0.3,3.6-0.6,4.7-0.8c1.4-0.3,2.5-1.6,2.5-3v-4.8C213.7,194.4,206.8,193.2,196.4,192.5z"></path>
</defs>
<clipPath id="SVGID_4_">
<use xlink:href="#SVGID_3_" overflow="visible"></use>
</clipPath>
<polygon opacity="0.24" clip-path="url(#SVGID_4_)" fill="#ED1C24" points="146.8,176.4 150.7,184.1 173.6,191.5 173.4,198.3
179.8,199.6 179.8,206.6 179.7,221.8 137.1,236.3 97.8,224.2 "></polygon>
</g>
<g>
<g>
<path fill="#ED912F" d="M146.9,172.5c0.9,1.2,0.9,2.8-0.1,3.9c-3.7,4.4-14,15.1-28,26.2c-14,11.1-26.8,18.7-31.9,21.3
c-1.3,0.7-2.9,0.3-3.8-0.8l-3-3.8l63.7-50.6L146.9,172.5z"></path>
</g>
<g>
<ellipse transform="matrix(0.7832 -0.6217 0.6217 0.7832 -96.3369 111.7049)" fill="#F9C441" cx="112"
cy="194" rx="40.7" ry="3.9"></ellipse>
</g>
</g>
</g>
<g id="mouth">
<g>
<path fill="#363284" d="M106.3,86.3c-1,0-1.9-0.1-2.9-0.4c-0.6-0.2-0.9-0.8-0.8-1.4c0.2-0.6,0.8-0.9,1.4-0.8
c2.2,0.6,4.4,0.3,6.4-0.7c2-1.1,3.4-2.9,4-5.1c0.2-0.6,0.8-0.9,1.4-0.8c0.6,0.2,0.9,0.8,0.8,1.4C115.2,83.2,110.9,86.3,106.3,86.3
z"></path>
</g>
</g>
<g id="right_eye_wink">
<g display="inline">
<path fill="#363284" d="M116.4,40.9c-0.4,0-0.7-0.2-0.9-0.5c-0.3-0.5-0.2-1.2,0.3-1.6c2.6-1.7,9.5-5.2,17.8-0.4
c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c-6.9-4-12.6-1.5-15.4,0.4C116.8,40.9,116.6,40.9,116.4,40.9z"></path>
</g>
<g display="inline">
<path fill="#363284" d="M130.7,55.9c-0.2,0-0.5-0.1-0.7-0.2c-4.1-3.1-11.8-2.8-11.9-2.8c-0.5,0-0.9-0.3-1.1-0.8
c-0.2-0.5,0-1,0.4-1.3c0.3-0.2,8.1-5.8,14.3-3.1c0.6,0.3,0.8,0.9,0.6,1.5c-0.3,0.6-0.9,0.8-1.5,0.6c-2.8-1.3-6.3-0.2-8.8,1
c2.9,0.3,6.7,1.1,9.4,3.1c0.5,0.4,0.6,1.1,0.2,1.6C131.4,55.8,131.1,55.9,130.7,55.9z"></path>
</g>
</g>
<g id="right_eye">
<g>
<path fill="#363284" d="M133.5,53.6c-0.6-0.3-1.2-0.6-1.8-0.9c0.1-0.5,0.2-0.9,0.2-1.4c0-4-3.2-7.3-7.3-7.3c-4,0-7.3,3.2-7.3,7.3
c0,0.6,0.1,1.2,0.2,1.7c-0.8,0.4-1.4,0.8-1.9,1.1c-0.5,0.3-0.7,1-0.3,1.6c0.2,0.3,0.6,0.5,0.9,0.5c0.2,0,0.4-0.1,0.6-0.2
c2.8-1.9,8.5-4.4,15.4-0.4c0.5,0.3,1.2,0.1,1.5-0.4C134.2,54.6,134,54,133.5,53.6z"></path>
<g>
<path fill="#FFFFFF"
d="M123,45.8c0.2,0.2,0.3,0.5,0.1,0.7s-0.4,0.2-0.6,0c-0.2-0.2-0.4-0.4-0.2-0.6S122.8,45.6,123,45.8z"></path>
</g>
<g>
<path fill="#FFFFFF" d="M122.2,47.5c0,0.6-0.5,0.4-1.1,0.4c-0.6,0-1.1,0.2-1.1-0.4c0-0.6,0.5-1.1,1.1-1.1
C121.7,46.5,122.2,46.9,122.2,47.5z"></path>
</g>
<g>
<path fill="#363284" d="M116.4,39.2c-0.4,0-0.7-0.2-0.9-0.5c-0.3-0.5-0.2-1.2,0.3-1.6c2.6-1.7,9.5-5.2,17.8-0.4
c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c-6.9-4-12.6-1.5-15.4,0.4C116.8,39.2,116.6,39.2,116.4,39.2z"></path>
</g>
</g>
</g>
<g id="left_eye">
<g>
<path fill="#363284" d="M85.5,53.6c-0.6-0.3-1.2-0.6-1.8-0.9c0.1-0.5,0.2-0.9,0.2-1.4c0-4-3.2-7.3-7.3-7.3c-4,0-7.3,3.2-7.3,7.3
c0,0.6,0.1,1.2,0.2,1.7c-0.8,0.4-1.4,0.8-1.9,1.1c-0.5,0.3-0.7,1-0.3,1.6c0.2,0.3,0.6,0.5,0.9,0.5c0.2,0,0.4-0.1,0.6-0.2
c2.8-1.9,8.5-4.4,15.4-0.4c0.5,0.3,1.2,0.1,1.5-0.4C86.2,54.6,86,54,85.5,53.6z"></path>
<g>
<path fill="#FFFFFF"
d="M75,45.8c0.2,0.2,0.3,0.5,0.1,0.7s-0.4,0.2-0.6,0c-0.2-0.2-0.4-0.4-0.2-0.6S74.8,45.6,75,45.8z"></path>
</g>
<g>
<path fill="#FFFFFF" d="M74.2,47.5c0,0.6-0.5,0.4-1.1,0.4c-0.6,0-1.1,0.2-1.1-0.4c0-0.6,0.5-1.1,1.1-1.1
C73.7,46.5,74.2,46.9,74.2,47.5z"></path>
</g>
<g>
<path fill="#363284" d="M68.4,39.2c-0.4,0-0.7-0.2-0.9-0.5c-0.3-0.5-0.2-1.2,0.3-1.6c2.6-1.7,9.5-5.2,17.8-0.4
c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c-6.9-4-12.6-1.5-15.4,0.4C68.8,39.2,68.6,39.2,68.4,39.2z"></path>
</g>
</g>
</g>
<g id="sunglasses">
<g>
<g>
<path fill="#F9C441" d="M35.7,30v1.8c0,0.9,0.9,1.3,1.8,1.3h4.2v6.2c0,15.4,12.4,27.9,27.8,27.9l-0.1,0c12.6,0,23.2-8,26.6-19.6
c0.7-2.5,2.7-4.5,5.2-5.1c3.7-0.8,7.2,1.5,8.2,4.9c3.3,11.3,13.2,19.2,25.2,19.8c16.2,0.7,29.1-12.7,29.1-28.9v-5.2h4.1
c0.9,0,1.9-0.4,1.9-1.3V30c0-0.9-1-1.9-1.9-1.9H37.5C36.6,28.1,35.7,29.1,35.7,30z"></path>
</g>
<g>
<defs>
<path id="SVGID_5_" d="M67.9,60.1c-11.7,0-22.2-8.9-22.2-20.8v-6.2h111v5.7c0,12.4-9.5,22.9-21.6,22.9l-0.1,0
c-9.9-0.2-17.8-6.4-20.6-15.8c-1.5-5.3-6.3-9.1-11.8-9.1c-5.5,0-10.3,2.8-11.9,8.2c-2.8,9.5-11.4,15-21.4,15H67.9z"></path>
</defs>
<clipPath id="SVGID_6_">
<use xlink:href="#SVGID_5_" overflow="visible"></use>
</clipPath>
<g clip-path="url(#SVGID_6_)">
<path fill="#FFFFFF"
d="M157.3,23.1h-9.2l-26.3,57h9.2L157.3,23.1z M145,23.1h-2.7l-26.3,57h2.7L145,23.1z"></path>
</g>
<g clip-path="url(#SVGID_6_)">
<path fill="#FFFFFF"
d="M88.4,23.1h-9.2l-26.3,57h9.2L88.4,23.1z M76.1,23.1h-2.7l-26.3,57h2.7L76.1,23.1z"></path>
</g>
</g>
</g>
</g>
</svg>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
h1 {
font-size: 1em;
font-family: "Montserrat";
text-transform: uppercase;
margin-bottom: 20px;
text-align: center;
color: #b6d0e0;
position: relative;
margin-top: -15px;
line-height: 15px;
}
.center {
margin: auto;
}
.wrapper {
width: 700px;
flex-shrink: 0;
background: white;
overflow: hidden;
height: 364px;
border-radius: 8px;
box-shadow: 0 1.5px 4px rgba(0, 0, 0, 0.24), 0 1.5px 6px rgba(0, 0, 0, 0.12);
}
.card-wrap {
width: 260px;
float: right;
background: #665aa7;
padding: 50px;
transform: scale(1.5) rotate(20deg);
}
.card {
transform: scale(0.75) rotate(-20deg);
}
svg {
width: 150%;
position: relative;
right: 130px;
top: 10px;
overflow: visible;
}
svg:not(:root) {
overflow: visible;
}
#sunglasses {
transition: transform 0.15s;
}
.checkout:hover ~ .card-wrap #sunglasses {
transform: translateY(-40px);
}
#right_eye_wink {
display: none;
}
.checkout:active ~ .card-wrap #right_eye {
display: none;
}
.checkout:active ~ .card-wrap #right_eye_wink {
display: block;
}
#mouth {
transition: transform 0.15s;
}
.checkout:hover ~ .card-wrap #mouth {
transform: translateY(-10px);
}
#front_hand, #back_hand {
transition: transform 0.03s;
}
.checkout:active ~ .card-wrap #front_hand, .checkout:active ~ .card-wrap #back_hand {
transform: translateX(10px);
}
.checkout {
outline: none;
background: #665aa7;
border: 0;
color: white;
position: relative;
top: 50%;
left: 15%;
transform: translateY(-50%);
padding: 12px 16px;
font-family: "Montserrat";
text-transform: uppercase;
font-size: 1.1em;
letter-spacing: 0.1em;
border-radius: 4px;
transition: all 0.1s ease-out;
box-shadow: 0 1.5px 4px rgba(0, 0, 0, 0.24), 0 1.5px 6px rgba(0, 0, 0, 0.12);
}
.checkout:hover:not(:active) {
background: #857bb9;
}
</style>

View File

@@ -0,0 +1,12 @@
<template>
<div style="display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 20px;">
<Card3D/>
</div>
</template>
<script setup lang="ts">
import Card3D from "@/components/3DCard/Card3D.vue";
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,11 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,35 @@
<template>
<div class="landing-header">
<AFlex :vertical="false" align-items="center" justify-content="space-between">
<div class="logo">
<img class="landing-header-logo" src="@/assets/svgs/logo-schisandra.svg" alt="logo">
</div>
</AFlex>
<AFlex :vertical="false" justify="space-between">
<div style="display: flex; align-items: center;justify-content: center;">
<div class="toggle-box" style="margin-top: 20px;">
<input @click="app.toggleDarkMode" v-model="isDarkMode" type="checkbox" name="checkbox1"
id="toggle-box-checkbox">
<label for="toggle-box-checkbox" class="toggle-box-label-left"></label>
<label for="toggle-box-checkbox" class="toggle-box-label"></label>
</div>
<AButton @click="router.push('/login')" type="primary" size="large" style="margin-right: 10px;">立即进入
</AButton>
</div>
</AFlex>
</div>
</template>
<script setup lang="ts">
import router from "@/router/router.ts";
import useStore from "@/store/index.ts";
import {ref} from "vue";
const app = useStore().theme;
const isDarkMode = ref<boolean>(app.darkMode === "dark");
</script>
<style src="./index.scss" scoped>
</style>

View File

@@ -0,0 +1,118 @@
.landing-header {
height: 100px;
width: 80%;
//background-color: #767779;
display: flex;
align-items: center;
justify-content: space-between;
.landing-header-logo {
display: inline-block;
margin-right: 1rem;
width: 50px;
height: 50px;
padding: 1rem;
}
.toggle-box-label-left:empty {
margin-left: -10px;
}
.toggle-box-label-left:before,
.toggle-box-label-left:after {
box-sizing: border-box;
margin: 0;
padding: 0;
/*transition*/
-webkit-transition: 0.25s ease-in-out;
-moz-transition: 0.25s ease-in-out;
-o-transition: 0.25s ease-in-out;
transition: 0.25s ease-in-out;
outline: none;
}
.toggle-box input[type=checkbox],
.toggle-box input[type=checkbox]:active {
position: absolute;
top: -5000px;
height: 0;
width: 0;
opacity: 0;
border: none;
outline: none;
}
.toggle-box label {
display: inline-block;
position: relative;
padding: 0px;
margin-bottom: 20px;
font-size: 14px;
line-height: 16px;
cursor: pointer;
color: rgba(149, 149, 149, 0.51);
font-weight: normal;
}
.toggle-box-label-left:before {
content: '';
display: block;
position: absolute;
z-index: 1;
line-height: 34px;
text-indent: 40px;
height: 16px;
width: 16px;
margin: 4px;
/*border-radius*/
-webkit-border-radius: 100%;
-moz-border-radius: 100%;
border-radius: 100%;
right: 26px;
bottom: 0px;
background: #FFB200;
transform: rotate(-45deg);
box-shadow: 0 0 10px white;
}
.toggle-box-label-left:after {
content: "";
display: inline-block;
width: 40px;
height: 24px;
/*border-radius*/
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.15);
vertical-align: middle;
margin: 0 10px;
border: 2px solid #FFB200;
}
.toggle-box input[type=checkbox]:checked + .toggle-box-label-left:before {
right: 17px;
box-shadow: 5px 5px 0 0 #eee;
background: transparent;
}
.toggle-box input[type=checkbox]:checked + .toggle-box-label-left:after {
background: rgba(0, 0, 0, 0.15);
border: 2px solid white;
}
.toggle-box input[type=checkbox] + .toggle-box-label-left {
color: rgba(250, 250, 250, 0.51);
font-weight: bold;
}
.toggle-box input[type=checkbox]:checked + .toggle-box-label-left {
color: rgba(149, 149, 149, 0.51);
font-weight: normal;
}
.toggle-box input[type=checkbox]:checked + .toggle-box-label-left + .toggle-box-label {
color: rgba(250, 250, 250, 0.51);
font-weight: bold;
}
}

View File

@@ -0,0 +1,154 @@
<template>
<AFlex :vertical="true" align="center" justify-content="center">
<Header/>
<Content/>
<Footer/>
<div class="area">
<ul class="circles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</AFlex>
</template>
<script setup lang="ts">
import Header from "@/layout/Landing/Header/Header.vue";
import Footer from "@/layout/Landing/Footer/Footer.vue";
import Content from "@/layout/Landing/Content/Content.vue";
</script>
<style scoped lang="scss">
.area {
background: #b9f187;
background: -webkit-linear-gradient(to left, #b9f187, #90d952,#70c13a,#52a82e);
width: 100%;
height: 100vh;
z-index: -1;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
}
.circles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.circles li {
position: absolute;
display: block;
list-style: none;
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.5);
animation: animate 25s linear infinite;
bottom: -150px;
}
.circles li:nth-child(1) {
left: 25%;
width: 80px;
height: 80px;
animation-delay: 0s;
}
.circles li:nth-child(2) {
left: 10%;
width: 20px;
height: 20px;
animation-delay: 2s;
animation-duration: 12s;
}
.circles li:nth-child(3) {
left: 70%;
width: 20px;
height: 20px;
animation-delay: 4s;
}
.circles li:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
animation-delay: 0s;
animation-duration: 18s;
}
.circles li:nth-child(5) {
left: 65%;
width: 20px;
height: 20px;
animation-delay: 0s;
}
.circles li:nth-child(6) {
left: 75%;
width: 110px;
height: 110px;
animation-delay: 3s;
}
.circles li:nth-child(7) {
left: 35%;
width: 150px;
height: 150px;
animation-delay: 7s;
}
.circles li:nth-child(8) {
left: 50%;
width: 25px;
height: 25px;
animation-delay: 15s;
animation-duration: 45s;
}
.circles li:nth-child(9) {
left: 20%;
width: 15px;
height: 15px;
animation-delay: 2s;
animation-duration: 35s;
}
.circles li:nth-child(10) {
left: 85%;
width: 150px;
height: 150px;
animation-delay: 0s;
animation-duration: 11s;
}
@keyframes animate {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
border-radius: 0;
}
100% {
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
border-radius: 50%;
}
}
</style>

View File

@@ -1,23 +0,0 @@
<template>
<AConfigProvider
:locale="lang.lang === 'en' ? enUS : zhCN"
:theme="app.themeConfig"
>
<router-view v-slot="{ Component, route }">
<transition name="animation" mode="out-in">
<component :is="Component" :key="route.path"/>
</transition>
</router-view>
</AConfigProvider>
</template>
<script setup lang="ts">
import enUS from 'ant-design-vue/es/locale/en_US';
import zhCN from 'ant-design-vue/es/locale/zh_CN';
import useStore from "@/store/index.ts";
const app = useStore().theme;
const lang = useStore().lang;
</script>
<style scoped lang="scss">
</style>

View File

@@ -23,7 +23,7 @@ export default {
passwordRule: "密码要6~18位字符且必须包含字母、数字和特殊符号",
phoneLoginAndRegister: "短信注册/登录",
open: "请打开",
scan: "扫码登录",
scan: "扫码关注公众号",
wechat: "微信",
repassword: "确认密码",
repasswordValidate: "请输入确认密码",

View File

@@ -1,20 +1,10 @@
import {useAuthStore} from '@/store/modules/userStore.ts';
import {useThemeStore} from "@/store/modules/themeStore.ts";
import {langStore} from "@/store/modules/langStore.ts";
import {useAuthSessionStore} from "@/store/modules/userSessionStore.ts";
export default function useStore() {
// 是否自动登录 默认自动化登录
function isAutoLogin() {
const result: string | null = localStorage.getItem('auto_login');
if (result) {
return result === 'true';
}
return true;
}
return {
user: isAutoLogin() ? useAuthStore() : useAuthSessionStore(), // 自动登录时使用 useAuthStore否则使用 useAuthSessionStore
user: useAuthStore(),
theme: useThemeStore(),
lang: langStore(),
};

View File

@@ -1,27 +0,0 @@
import {defineStore} from 'pinia';
import {reactive} from 'vue';
export const useAuthSessionStore = defineStore(
'user',
() => {
const user: any = reactive({
accessToken: '',
uid: '',
refreshToken: '',
expiresAt: 0,
});
return {
user,
};
},
{
// 开启数据持久化
persist: {
key: 'user',
storage: sessionStorage,
paths: ['user'],
}
}
);

2
src/types/user.d.ts vendored
View File

@@ -1,11 +1,13 @@
export interface AccountLogin {
account?: string
password?: string;
auto_login?: boolean;
}
export interface PhoneLogin {
phone?: string
captcha?: string;
auto_login?: boolean;
}
export interface ResetPassword {

View File

@@ -1,17 +1,11 @@
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
name: "LandingPage"
});
</script>
<template>
<div>
<h1>Welcome to our website!</h1>
<LandingLayout/>
</div>
</template>
<script setup lang="ts">
import LandingLayout from "@/layout/Landing/index.vue";
</script>
<style scoped>
</style>

View File

@@ -6,7 +6,13 @@
router.push('/qrlogin')
}" class="login-form-bottom-button" :icon="h(QrcodeOutlined)">{{ t("login.qrLogin") }}
</AButton>
<AButton @click="openGiteeUrl" class="login-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton class="login-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton @click="openGiteeUrl" class="login-form-bottom-button"
style="display: flex; align-items: center;justify-content: center;">
<template #icon>
<img :src=gitee alt="gitee" class="gitee-icon" style="width: 16px; height: 16px;"/>
</template>
</AButton>
<AButton @click="openGithubUrl" class="login-form-bottom-button" :icon="h(GithubOutlined)"></AButton>
</AFlex>
</div>
@@ -20,17 +26,22 @@ import {getGithubUrl} from "@/api/oauth/github.ts";
import {getGiteeUrl} from "@/api/oauth/gitee.ts";
import useStore from "@/store";
import {message} from "ant-design-vue";
import gitee from "@/assets/svgs/gitee.svg";
import {generateClientId} from "@/api/oauth/wechat.ts";
import {getQQUrl} from "@/api/oauth/qq.ts";
const router = useRouter();
const {t} = useI18n();
const githubRedirectUrl = ref<string>('');
const giteeRedirectUrl = ref<string>('');
const qqRedirectUrl = ref<string>('');
/**
* Get the redirect url of Github OAuth
*/
async function getGithubRedirectUrl() {
const res: any = await getGithubUrl();
const clientId: string = getLocalClientId() as string;
const res: any = await getGithubUrl(clientId);
if (res.code === 0 && res.data) {
githubRedirectUrl.value = res.data;
}
@@ -46,6 +57,43 @@ async function getGiteeRedirectUrl() {
}
}
/**
* Get the redirect url of QQ OAuth
*/
async function getQQRedirectUrl() {
const clientId: string = getLocalClientId() as string;
const res: any = await getQQUrl(clientId);
if (res.code === 0 && res.data) {
qqRedirectUrl.value = res.data;
}
}
/**
* 获取client_id
*/
async function getClientId() {
const id: string | null = localStorage.getItem('client_id');
if (!id) {
const res: any = await generateClientId();
if (res.code === 0 && res.data) {
localStorage.setItem('client_id', res.data);
}
}
}
/**
* 获取本地client_id
*/
function getLocalClientId(): string | null {
const clientID: string | null = localStorage.getItem('client_id');
if (clientID) {
return clientID;
} else {
getClientId();
return localStorage.getItem('client_id');
}
}
/**
* Open the Github OAuth page in a new window
*/
@@ -106,6 +154,7 @@ const openGiteeUrl = () => {
onMounted(() => {
getGithubRedirectUrl();
getGiteeRedirectUrl();
getQQRedirectUrl();
});
</script>
<style src="./index.scss" scoped>

View File

@@ -52,11 +52,9 @@
</AFlex>
</AFormItem>
<AFormItem>
<AFormItem id="phone_login_auto" name="auto_login">
<AFlex :vertical="false" justify="space-between">
<ACheckbox @change="(e: any)=>{
autoLoginChang(e.target.checked);
}" v-model:checked="autoLoginChecked">{{ t("login.autoLogin") }}
<ACheckbox v-model:checked="phoneLoginForm.auto_login">{{ t("login.autoLogin") }}
</ACheckbox>
</AFlex>
@@ -101,11 +99,9 @@
</AInputPassword>
</AFlex>
</AFormItem>
<AFormItem>
<AFormItem id="account_login_auto" name="auto_login">
<AFlex :vertical="false" justify="space-between">
<ACheckbox v-model:checked="autoLoginChecked" @change="(e: any)=>{
autoLoginChang(e.target.checked);
}">{{ t("login.autoLogin") }}
<ACheckbox v-model:checked="accountLoginForm.auto_login">{{ t("login.autoLogin") }}
</ACheckbox>
<a @click="()=>{
router.push('/resetpass')
@@ -162,7 +158,7 @@
</template>
<script setup lang="ts">
import {Rule} from "ant-design-vue/lib/form";
import {onBeforeMount, onMounted, reactive, ref, UnwrapRef} from "vue";
import {onMounted, reactive, ref, UnwrapRef} from "vue";
import {AccountLogin, PhoneLogin} from "@/types/user";
import {useI18n} from "vue-i18n";
import BoxDog from "@/components/BoxDog/BoxDog.vue";
@@ -181,7 +177,6 @@ const showPhoneRotateCaptcha = ref<boolean>(false);
const showAccountRotateCaptcha = ref<boolean>(false);
const captchaData = reactive({angle: 0, image: "", thumb: "", key: ""});
const captchaErrorCount = ref<number>(0);
const autoLoginChecked = ref<boolean>(localStorage.getItem('auto_login') === 'true');
const phoneLoginRotateEvent = {
confirm: (angle: number) => {
checkPhoneLoginCaptcha(angle);
@@ -208,11 +203,13 @@ const accountLoginRotateEvent = {
const accountLoginForm: UnwrapRef<AccountLogin> = reactive({
account: '',
password: '',
auto_login: true,
});
// 手机登录表单数据
const phoneLoginForm: UnwrapRef<PhoneLogin> = reactive({
phone: '',
captcha: '',
auto_login: true,
});
// 表单验证提示
const accountValidate = ref<string>(t('login.accountValidate'));
@@ -442,21 +439,6 @@ async function sendMessageByPhone(): Promise<boolean> {
return false;
}
}
/**
* 自动登录
* @param checkedValue
*/
async function autoLoginChang(checkedValue: boolean) {
return localStorage.setItem('auto_login', String(checkedValue));
}
onBeforeMount(() => {
const autoLogin: string | null = localStorage.getItem('auto_login');
if (!autoLogin) {
localStorage.setItem('auto_login', 'true');
}
});
</script>
<style src="./index.scss" scoped>
@import "@/assets/styles/global.scss";

View File

@@ -1,7 +1,6 @@
.login-main {
display: flex;
flex-direction: row;
//background-color: rgb(238, 255, 238);
width: 100vw;
height: 100vh;
/* 加载背景图 */

View File

@@ -15,7 +15,7 @@
</span>
<AQrcode
class="qrlogin-card-qr"
:size="230"
:size="280"
:error-level="'H'"
:status="status"
@refresh="() => {
@@ -24,7 +24,6 @@
:value=qrcode
:icon="logo"
/>
<ACheckbox class="qrlogin-card-auto-login">{{ t("login.autoLogin") }}</ACheckbox>
</AFlex>
<QRLoginFooter/>
<ATooltip placement="left">
@@ -44,7 +43,7 @@ import {useI18n} from "vue-i18n";
import BoxDog from "@/components/BoxDog/BoxDog.vue";
import QRLoginFooter from "@/views/QRLogin/QRLoginFooter.vue";
import {useRouter} from 'vue-router';
import {closeWebsocket, generateClientId, generateQrCode} from "@/api/oauth/wechat.ts";
import {generateClientId, generateQrCode} from "@/api/oauth/wechat.ts";
import {onMounted, onUnmounted, ref} from "vue";
import logo from "@/assets/svgs/logo-schisandra.svg";
import useWebSocket from "@/utils/websocket/websocket.ts";
@@ -80,30 +79,29 @@ async function getQrCode() {
if (!clientId) {
status.value = 'expired';
await getClientId();
return;
}
const res: any = await generateQrCode(clientId);
if (res.code === 0 && res.data) {
status.value = 'active';
qrcode.value = res.data;
localStorage.setItem('qr_code', res.data);
await getQrCode();
} else {
status.value = 'expired';
const res: any = await generateQrCode(clientId);
if (res.code === 0 && res.data) {
status.value = 'active';
qrcode.value = res.data;
} else {
status.value = 'expired';
}
}
}
/**
* 获取本地client_id
*/
function getLocalClientId(): string {
function getLocalClientId(): string | null {
const clientID: string | null = localStorage.getItem('client_id');
if (clientID) {
return clientID;
} else {
getClientId();
return getLocalClientId();
return localStorage.getItem('client_id');
}
}
const wsOptions = {
@@ -113,13 +111,10 @@ const wsOptions = {
const {open, close, on} = useWebSocket(wsOptions);
onMounted(async () => {
await getClientId();
await getQrCode();
open();
// 注册消息接收处理函数
on('message', (data: any) => {
console.log(data);
if (data.code === 0 && data.data) {
const user = useStore().user;
const {access_token, refresh_token, uid, expires_at} = data.data;
@@ -134,9 +129,7 @@ onMounted(async () => {
});
onUnmounted(async () => {
await closeWebsocket(getLocalClientId());
close(true);
});
</script>
<style src="./index.scss" scoped>

View File

@@ -6,7 +6,13 @@
router.push('/login')
}" class="qrlogin-form-bottom-button" :icon="h(TabletOutlined)">{{ t("login.phoneLoginAndRegister") }}
</AButton>
<AButton @click="openGiteeUrl" class="qrlogin-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton class="qrlogin-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton @click="openGiteeUrl" class="qrlogin-form-bottom-button"
style="display: flex; align-items: center;justify-content: center;">
<template #icon>
<img :src=gitee alt="gitee" class="gitee-icon" style="width: 16px; height: 16px;"/>
</template>
</AButton>
<AButton @click="openGithubUrl" class="qrlogin-form-bottom-button" :icon="h(GithubOutlined)"></AButton>
</AFlex>
</div>
@@ -20,18 +26,23 @@ import {getGithubUrl} from "@/api/oauth/github.ts";
import {getGiteeUrl} from "@/api/oauth/gitee.ts";
import useStore from "@/store";
import {message} from "ant-design-vue";
import gitee from "@/assets/svgs/gitee.svg";
import {generateClientId} from "@/api/oauth/wechat.ts";
import {getQQUrl} from "@/api/oauth/qq.ts";
const router = useRouter();
const {t} = useI18n();
const githubRedirectUrl = ref<string>('');
const giteeRedirectUrl = ref<string>('');
const qqRedirectUrl = ref<string>('');
/**
* Get the redirect url of Github OAuth
*/
async function getGithubRedirectUrl() {
const res: any = await getGithubUrl();
const clientId: string = getLocalClientId() as string;
const res: any = await getGithubUrl(clientId);
if (res.code === 0 && res.data) {
githubRedirectUrl.value = res.data;
}
@@ -47,6 +58,43 @@ async function getGiteeRedirectUrl() {
}
}
/**
* 获取client_id
*/
async function getClientId() {
const id: string | null = localStorage.getItem('client_id');
if (!id) {
const res: any = await generateClientId();
if (res.code === 0 && res.data) {
localStorage.setItem('client_id', res.data);
}
}
}
/**
* Get the redirect url of QQ OAuth
*/
async function getQQRedirectUrl() {
const clientId: string = getLocalClientId() as string;
const res: any = await getQQUrl(clientId);
if (res.code === 0 && res.data) {
qqRedirectUrl.value = res.data;
}
}
/**
* 获取本地client_id
*/
function getLocalClientId(): string | null {
const clientID: string | null = localStorage.getItem('client_id');
if (clientID) {
return clientID;
} else {
getClientId();
return localStorage.getItem('client_id');
}
}
/**
* Open the Github OAuth page in a new window
*/
@@ -107,6 +155,7 @@ const openGiteeUrl = () => {
onMounted(() => {
getGithubRedirectUrl();
getGiteeRedirectUrl();
getQQRedirectUrl();
});
</script>
<style src="./index.scss" scoped>