✨ qq oauth2 login complete
This commit is contained in:
@@ -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'
|
||||
|
@@ -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
6
components.d.ts
vendored
@@ -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']
|
||||
|
@@ -6,7 +6,7 @@ $modes: (
|
||||
infoColor: #000
|
||||
),
|
||||
"dark": (
|
||||
bgColor: #000,
|
||||
bgColor: rgba(15, 15, 16, 0.63),
|
||||
infoColor: #fff
|
||||
)
|
||||
);
|
||||
|
@@ -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");
|
||||
|
||||
|
11
src/router/modules/main_router.ts
Normal file
11
src/router/modules/main_router.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default [
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
component: () => import('@/views/Main/MainPage.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '主页'
|
||||
}
|
||||
}
|
||||
];
|
@@ -1,11 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
path: '/test2',
|
||||
name: 'test2',
|
||||
component: () => import('@/views/TestI18n.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '测试'
|
||||
}
|
||||
}
|
||||
];
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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'));
|
||||
}
|
||||
|
21
src/views/Main/MainPage.vue
Normal file
21
src/views/Main/MainPage.vue
Normal 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>
|
@@ -138,6 +138,9 @@ onMounted(async () => {
|
||||
user.user.expiresAt = expires_at;
|
||||
status.value = 'scanned';
|
||||
message.success(t('login.loginSuccess'));
|
||||
setTimeout(() => {
|
||||
router.push('/main');
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -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();
|
||||
|
@@ -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>
|
Reference in New Issue
Block a user