qq oauth2 login complete

This commit is contained in:
landaiqing
2024-08-21 19:40:50 +08:00
parent 7683fffb34
commit 95f722fa93
14 changed files with 165 additions and 76 deletions

View File

@@ -2,13 +2,13 @@
VITE_NODE_ENV='development'
# 开发环境
VITE_APP_BASE_API='/api'
VITE_APP_BASE_API='/sys'
# 页面 title 前缀
VITE_APP_TITLE=开发环境
# 网络请求公用地址
VITE_API_BASE_URL='http://127.0.0.1:8080'
VITE_API_BASE_URL='http://127.0.0.1:80'
VITE_TITLE_NAME='五味子云相册'
@@ -16,4 +16,5 @@ VITE_TITLE_NAME='五味子云相册'
VITE_APP_TOKEN_KEY='Bearer'
# the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/gws'
#VITE_WEB_SOCKET_URL='ws://127.0.0.1:80/api/ws/gws'
VITE_WEB_SOCKET_URL='wss://landaiqing.cn/api/ws/gws'

View File

@@ -1,13 +1,13 @@
# 生产环境配置
VITE_NODE_ENV='production'
# 生产环境
VITE_APP_BASE_API='/api'
VITE_APP_BASE_API='/sys'
# 页面 title 前缀
VITE_APP_TITLE=生产环境
# 网络请求公用地址
VITE_API_BASE_URL='http://1.95.0.111:5050'
VITE_API_BASE_URL='http://127.0.0.1:80'
VITE_TITLE_NAME='五味子云相册'
@@ -15,4 +15,5 @@ VITE_TITLE_NAME='五味子云相册'
VITE_APP_TOKEN_KEY='Bearer'
# the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:8080/api/ws/gws'
#VITE_WEB_SOCKET_URL='ws://127.0.0.1:80/api/ws/gws'
VITE_WEB_SOCKET_URL='wss://landaiqing.cn/api/ws/gws'

6
components.d.ts vendored
View File

@@ -12,7 +12,6 @@ declare module 'vue' {
ACard: typeof import('ant-design-vue/es')['Card']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADivider: typeof import('ant-design-vue/es')['Divider']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AFlex: typeof import('ant-design-vue/es')['Flex']
@@ -23,17 +22,14 @@ declare module 'vue' {
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AQrcode: typeof import('ant-design-vue/es')['QRCode']
ArrowRightOutlined: typeof import('@ant-design/icons-vue')['ArrowRightOutlined']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']
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/Card3D/Card3D.vue')['default']
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
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']
@@ -42,6 +38,7 @@ declare module 'vue' {
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
LoginPage: typeof import('./src/views/Login/LoginPage.vue')['default']
MainPage: typeof import('./src/views/Main/MainPage.vue')['default']
NotFound: typeof import('./src/views/404/NotFound.vue')['default']
QqOutlined: typeof import('@ant-design/icons-vue')['QqOutlined']
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
@@ -50,7 +47,6 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
TestI18n: typeof import('./src/views/TestI18n.vue')['default']
TestTheme: typeof import('./src/views/TestTheme.vue')['default']
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
WechatOutlined: typeof import('@ant-design/icons-vue')['WechatOutlined']

View File

@@ -6,7 +6,7 @@ $modes: (
infoColor: #000
),
"dark": (
bgColor: #000,
bgColor: rgba(15, 15, 16, 0.63),
infoColor: #fff
)
);

View File

@@ -16,7 +16,27 @@
<label for="toggle-box-checkbox" class="toggle-box-label-left"></label>
<label for="toggle-box-checkbox" class="toggle-box-label"></label>
</div>
<div style="margin-right: 20px;">
<ADropdown>
<template #overlay>
<AMenu @click="(e: any)=>{
changeTheme(e.key)
}">
<AMenuItem v-for="(color, name) in variables" :key="name" :value="color">
<div
v-bind:style="{ backgroundColor: name as string, width: '20px', height: '20px', borderRadius: '20%' }">
</div>
</AMenuItem>
</AMenu>
</template>
<AButton type="text" size="large">
<div
v-bind:style="{ backgroundColor: app.themeName, width: '20px', height: '20px', borderRadius: '20%' }">
</div>
</AButton>
</ADropdown>
</div>
<div style="margin-right: 20px;">
<ADropdown>
<template #overlay>
@@ -34,6 +54,7 @@
</ADropdown>
</div>
<AButton @click="router.push('/login')" type="primary" size="large" style="margin-right: 10px;">
{{ t("landing.immediately") }}
</AButton>
@@ -49,6 +70,7 @@ import useStore from "@/store/index.ts";
import {ref} from "vue";
import {DownOutlined} from '@ant-design/icons-vue';
import {useI18n} from "vue-i18n";
import variables from "@/assets/styles/colors.module.scss";
const lang = useStore().lang;
const {t, locale} = useI18n();
@@ -59,6 +81,10 @@ async function changeLang(language: any) {
locale.value = language;
}
async function changeTheme(theme: any) {
app.setThemeName(theme);
}
const app = useStore().theme;
const isDarkMode = ref<boolean>(app.darkMode === "dark");

View File

@@ -0,0 +1,11 @@
export default [
{
path: '/main',
name: 'main',
component: () => import('@/views/Main/MainPage.vue'),
meta: {
requiresAuth: true,
title: '主页'
}
}
];

View File

@@ -1,11 +0,0 @@
export default [
{
path: '/test2',
name: 'test2',
component: () => import('@/views/TestI18n.vue'),
meta: {
requiresAuth: true,
title: '测试'
}
}
];

View File

@@ -3,20 +3,21 @@
import {createRouter, createWebHistory, Router, RouteRecordRaw} from 'vue-router';
import login from './modules/login';
import test from "@/router/modules/test.ts";
import test2 from "@/router/modules/testI18n.ts";
import useStore from "@/store";
import {message} from "ant-design-vue";
import {close, start} from '@/components/Nprogress/nprogress.ts';
import notFound from "@/router/modules/notFound.ts";
import landing from "@/router/modules/landing.ts";
import mainRouter from "@/router/modules/main_router.ts";
const routes: Array<RouteRecordRaw> = [
...login,
...notFound,
...landing,
...mainRouter,
...test,
...test2,
{
path: '/:pathMatch(.*)',
redirect: '/404',
@@ -29,7 +30,7 @@ const router: Router = createRouter({
routes
});
router.beforeEach((to, from, next) => {
router.beforeEach((to, _from, next) => {
start();
if (to.meta.requiresAuth) {
const user = useStore().user;

View File

@@ -6,7 +6,7 @@
router.push('/qrlogin')
}" class="login-form-bottom-button" :icon="h(QrcodeOutlined)">{{ t("login.qrLogin") }}
</AButton>
<AButton class="login-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton @click="openQQUrl" 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>
@@ -20,7 +20,7 @@
<script setup lang="ts">
import {GithubOutlined, QqOutlined, QrcodeOutlined} from "@ant-design/icons-vue";
import {useI18n} from "vue-i18n";
import {h, onMounted, ref} from "vue";
import {h, onBeforeMount, ref} from "vue";
import {useRouter} from 'vue-router';
import {getGithubUrl} from "@/api/oauth/github.ts";
import {getGiteeUrl} from "@/api/oauth/gitee.ts";
@@ -117,6 +117,9 @@ const openGithubUrl = () => {
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
@@ -146,12 +149,47 @@ const openGiteeUrl = () => {
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
window.addEventListener("message", messageHandler);
};
onMounted(() => {
/**
* Open the QQ OAuth page in a new window
*/
const openQQUrl = () => {
const iWidth = 800; //弹出窗口的宽度;
const iHeight = 500; //弹出窗口的高度;
//window.screen.height获得屏幕的高window.screen.width获得屏幕的宽
const iTop = (window.screen.height - 30 - iHeight) / 2; //获得窗口的垂直位置;
const iLeft = (window.screen.width - 10 - iWidth) / 2; //获得窗口的水平位置;
window.open(qqRedirectUrl.value, 'newwindow', 'height=' + iHeight + ',innerHeight=' + iHeight + ',width=' + iWidth + ',innerWidth=' + iWidth + ',top=' + iTop + ',left=' + iLeft + ',toolbar=no,menubar=no,scrollbars=auto,resizable=no,location=no,status=no');
const messageHandler = (e: any) => {
if (typeof e.data === 'string') {
const data: any = JSON.parse(e.data);
if (data.code === 0 && data.data) {
const user = useStore().user;
const {access_token, refresh_token, uid, expires_at} = data.data;
user.user.accessToken = access_token;
user.user.refreshToken = refresh_token;
user.user.uid = uid;
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
window.addEventListener("message", messageHandler);
};
onBeforeMount(() => {
getGithubRedirectUrl();
getGiteeRedirectUrl();
getQQRedirectUrl();

View File

@@ -355,6 +355,9 @@ async function phoneLoginSubmit() {
userStore.user.refreshToken = refresh_token;
userStore.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
setTimeout(() => {
router.push('/main');
}, 1000);
} else {
message.error(res.message);
}
@@ -433,6 +436,9 @@ async function checkAccountLoginCaptcha(angle: number) {
userStore.user.refreshToken = refresh_token;
userStore.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
setTimeout(() => {
router.push('/main');
}, 1000);
} else {
message.error(t('login.loginError'));
}

View File

@@ -0,0 +1,21 @@
<template>
<div>
<h1>Welcome to Main Page</h1>
<AButton @click="handleClick">获取用户数据</AButton>
{{ data }}
</div>
</template>
<script setup lang="ts">
import {useRequest} from "alova/client";
import {getUserInfo} from "@/api/user";
const {data, send} = useRequest(getUserInfo, {
immediate: false
});
const handleClick = () => {
send();
};
</script>
<style scoped>
</style>

View File

@@ -138,6 +138,9 @@ onMounted(async () => {
user.user.expiresAt = expires_at;
status.value = 'scanned';
message.success(t('login.loginSuccess'));
setTimeout(() => {
router.push('/main');
}, 1000);
}
});
});

View File

@@ -6,7 +6,7 @@
router.push('/login')
}" class="qrlogin-form-bottom-button" :icon="h(TabletOutlined)">{{ t("login.phoneLoginAndRegister") }}
</AButton>
<AButton class="qrlogin-form-bottom-button" :icon="h(QqOutlined)"></AButton>
<AButton @click="openQQUrl" 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>
@@ -20,7 +20,7 @@
<script setup lang="ts">
import {GithubOutlined, QqOutlined, TabletOutlined} from "@ant-design/icons-vue";
import {useI18n} from "vue-i18n";
import {h, onMounted, ref} from "vue";
import {h, onBeforeMount, ref} from "vue";
import {useRouter} from 'vue-router';
import {getGithubUrl} from "@/api/oauth/github.ts";
import {getGiteeUrl} from "@/api/oauth/gitee.ts";
@@ -118,6 +118,9 @@ const openGithubUrl = () => {
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
@@ -147,12 +150,48 @@ const openGiteeUrl = () => {
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
window.addEventListener("message", messageHandler);
};
onMounted(() => {
/**
* Open the QQ OAuth page in a new window
*/
const openQQUrl = () => {
const iWidth = 800; //弹出窗口的宽度;
const iHeight = 500; //弹出窗口的高度;
//window.screen.height获得屏幕的高window.screen.width获得屏幕的宽
const iTop = (window.screen.height - 30 - iHeight) / 2; //获得窗口的垂直位置;
const iLeft = (window.screen.width - 10 - iWidth) / 2; //获得窗口的水平位置;
window.open(qqRedirectUrl.value, 'newwindow', 'height=' + iHeight + ',innerHeight=' + iHeight + ',width=' + iWidth + ',innerWidth=' + iWidth + ',top=' + iTop + ',left=' + iLeft + ',toolbar=no,menubar=no,scrollbars=auto,resizable=no,location=no,status=no');
const messageHandler = (e: any) => {
if (typeof e.data === 'string') {
const data: any = JSON.parse(e.data);
if (data.code === 0 && data.data) {
const user = useStore().user;
const {access_token, refresh_token, uid, expires_at} = data.data;
user.user.accessToken = access_token;
user.user.refreshToken = refresh_token;
user.user.uid = uid;
user.user.expiresAt = expires_at;
message.success(t('login.loginSuccess'));
window.removeEventListener("message", messageHandler);
setTimeout(() => {
router.push('/main');
}, 1000);
}
}
};
window.addEventListener("message", messageHandler);
};
onBeforeMount(() => {
getGithubRedirectUrl();
getGiteeRedirectUrl();
getQQRedirectUrl();

View File

@@ -1,43 +0,0 @@
<template>
<div class="test">
<AButton @click="handleClick"> {{ t('login.title') }}</AButton>
<AButton @click="changeLang('zh')"> 切换中文</AButton>
<AButton @click="changeLang('en')"> 切换英文</AButton>
<ADatePicker/>
<ATimePicker/>
{{ data }}
</div>
</template>
<script setup lang="ts">
import {useRequest} from "alova/client";
import {getUserInfo} from "@/api/user";
import {useI18n} from "vue-i18n";
import useStore from "@/store";
const {t, locale} = useI18n();
const {data, send} = useRequest(getUserInfo, {
immediate: false
});
const handleClick = () => {
send();
};
const lang = useStore().lang;
async function changeLang(language: any) {
lang.lang = language;
locale.value = language;
}
</script>
<style lang="scss" scoped>
@import "@/assets/styles/theme.scss";
.test {
@include useTheme {
background: getModeVar('primary');
color: getColor('info');
}
}
</style>