From 64126d55d85aab42b2d946be7f138a0677bbf2a7 Mon Sep 17 00:00:00 2001 From: junleea <354425203@qq.com> Date: Sun, 27 Apr 2025 13:09:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC=E4=B8=89=E6=96=B9?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/user.ts | 55 +++++++++++ src/components/upload-file.vue | 7 +- src/types/user.ts | 17 ++++ src/utils/request2.ts | 2 - src/views/pages/login.vue | 89 ++++++++++++++++++ src/views/pages/ucenter.vue | 141 ++++++++++++++++++++++++++++ src/views/system/gen-chat.vue | 63 ++++++++++++- src/views/system/manage-file.vue | 4 +- src/views/system/manage-session.vue | 2 +- 9 files changed, 369 insertions(+), 11 deletions(-) diff --git a/src/api/user.ts b/src/api/user.ts index 60300f1..3ac9f89 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -166,6 +166,61 @@ export const genResetPassword = (Data) => { return request.post('/user/reset', params) } +//获取第三方登录uuid +export const getThirdPartyUUID = (data) => { + let url ='/user/oAuth_uuid' + "?type=" + data["type"] + return request.get(url) +} + +//获取第三方登录的url +export const getThirdPartyLoginUrl = (Data) => { + let url ='/tool/get_auth_url' + "?uuid=" + Data.uuid + "&type=" + Data["type"] + "&platform=" + Data["platform"] + if (Data["type"] === "add" ) { + url += "&token=" + Data["token"] + } + return request.get(url) + +} + +//获取绑定的第三方登录信息 +export const getThirdPartyInfo = (Data) => { + const params = new URLSearchParams(); + //console.log("qrdata=",qrData); + // for (let key in Data) { + // params.append(key, Data[key]) + // } + return request.post('/user/third_party_login_list', params ,{ + headers: { + 'token': Data.token, + } + }) +} +export const deleteThirdPartyInfo = (Data) => { + let url ='/user/delete_third_party_login' + "?id=" + Data.id + return request.delete(url, { + headers: { + 'token': Data.token, + } + }) +} + +//获取qq登录的url +export const getQQLoginUrl = (Data) => { + let url ='/tool/qq_auth' + "?uuid=" + Data.uuid + "&type=" + Data["type"] + return request.get(url) +} +//获取github登录的url +export const getGithubLoginUrl = (Data) => { + let url ='/tool/github_auth' + "?uuid=" + Data.uuid + "&type=" + Data["type"] + return request.get(url) +} + +//获取第三方登录状态 +export const getThirdPartyLoginStatus = (Data) => { + let url ='/user/oAuth' + "?uuid=" + Data.uuid + return request.get(url) +} + export const fetchUserData = () => { return { "list": [ diff --git a/src/components/upload-file.vue b/src/components/upload-file.vue index 453c85f..4d20736 100644 --- a/src/components/upload-file.vue +++ b/src/components/upload-file.vue @@ -62,8 +62,6 @@ const headers = { }; const beforeUpload = (file: any) => { - if(userRole == "admin"){ - } const fileExtension = file.name.split('.').pop().toLowerCase(); const isAllowedType = allowedTypes.includes(fileExtension); if (!isAllowedType && userRole != "admin") { @@ -75,13 +73,14 @@ const headers = { confirm('不允许的文件类型,是否继续上传?') ? isAllowedType : false; } // 可以在这里进行文件验证等操作 - const isLt2M = file.size / 1024 / 1024 < 5; + let isLt2M = file.size / 1024 / 1024 < 5; if (!isLt2M && userRole != "admin") { ElMessage.error('上传文件大小不能超过 5MB'); } if(!isLt2M && userRole == "admin"){ //提示文件过大,是否继续上传,确认后继续上传 - confirm('文件过大,是否继续上传?') ? isLt2M : false; + let res = confirm('文件过大,是否继续上传?') ; + isLt2M =res; } return isLt2M; }; diff --git a/src/types/user.ts b/src/types/user.ts index b38d0e7..a72c828 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -49,4 +49,21 @@ export interface UserInfo { Gender: string; Role: string; Avatar: string; +} + + +export interface ThirdPartyUserInfo { + // 对应 gorm.Model,由于 gorm.Model 一般包含创建时间、更新时间等字段,这里暂不详细展开,仅表示有公共的基础属性 + // 如果项目中有明确的基础属性定义,可以继承相应的基础接口 + // 这里假设基础属性为空,仅包含以下自定义属性 + ID: number; // 第三方用户ID + CreatedAt: string; // 创建时间 + UpdatedAt: string; // 更新时间 + DeletedAt: string; // 删除时间 + user_id: number; // 用户ID,本系统的用户id + third_party_id: number; // 第三方用户ID + third_party_platform: string; // 第三方平台名称,qq,github + third_party_user_name: string; // 第三方用户名 + third_party_user_avatar: string; // 第三方用户头像 + third_party_user_url: string; // 第三方用户主页,可选 } \ No newline at end of file diff --git a/src/utils/request2.ts b/src/utils/request2.ts index a11663a..5483add 100644 --- a/src/utils/request2.ts +++ b/src/utils/request2.ts @@ -41,7 +41,5 @@ request.interceptors.response.use( return Promise.reject(error); } ) -request.interceptors.request.use( -) export default request; \ No newline at end of file diff --git a/src/views/pages/login.vue b/src/views/pages/login.vue index 5964b14..2cbd63a 100644 --- a/src/views/pages/login.vue +++ b/src/views/pages/login.vue @@ -51,6 +51,24 @@ >

+
+ + 第三方登录 +
+ + +
+
@@ -62,6 +80,7 @@ import { ElMessage } from "element-plus"; import { loginService } from "@/api/user"; import { GetUserInfoService } from "@/api/user"; import { usePermissStore } from "@/store/permiss"; +import {getThirdPartyUUID,getThirdPartyLoginStatus,getGithubLoginUrl,getQQLoginUrl,getThirdPartyLoginUrl} from "@/api/user"; // 从本地存储获取登录参数 const lgStr = localStorage.getItem("login-param"); const defParam = lgStr ? JSON.parse(lgStr) : null; @@ -77,6 +96,8 @@ const param = reactive({ password: defParam ? defParam.password : "", }); +const qqButtonBgImage = ref('https://wiki.connect.qq.com/wp-content/uploads/2016/12/Connect_logo_4.png'); + // 表单验证规则 const rules = { username: [ @@ -105,6 +126,74 @@ var loginData = ref({ ip: "", }); +const thirdLogin = async (type) => { + //获取uuid + let uuidResp = await getThirdPartyUUID({"type": type}); + if (uuidResp["code"] !== 0) { + ElMessage.error("获取UUID失败!请稍后再试"); + return; + } + let uuid = uuidResp["data"]; + if (!uuid) { + ElMessage.error("获取UUID失败!请稍后再试"); + return; + } + let result={}; + if(type !== "github" && type !== "qq") { + + ElMessage.error("不支持的登录平台!请稍后再试:"+type); + return; + } + result = await getThirdPartyLoginUrl({uuid: uuid,"type": "login", "platform": type }); + if (result["code"] !== 0) { + ElMessage.error("获取登录地址失败!请稍后再试"); + return; + } + let loginUrl = result["data"]; + if (!loginUrl) { + ElMessage.error("获取登录地址失败!请稍后再试"); + return; + } + //获取登录状态每2秒请求一次 + let timer = setInterval(async () => { + let statusResp = await getThirdPartyLoginStatus({uuid: uuid}); + if (statusResp["code"] !== 0) { + ElMessage.error("获取登录状态失败!请稍后再试"); + clearInterval(timer); + return; + } + if(statusResp["data"]["status"] === 163) { + ElMessage.error("该账号未绑定,请先绑定账号!"); + clearInterval(timer); + return; + } + let status = statusResp["data"]; + if(status["status"] === 0) { + //登录成功 + clearInterval(timer); + let userInfo = status["user_info"]; + let token = userInfo["token"]; + if (!token) { + ElMessage.error("获取登录状态失败!请稍后再试"); + return; + } + + globalData["token"] = token; + localStorage.setItem("token", token); + localStorage.setItem("userId", userInfo["id"]); + localStorage.setItem("username", status["username"]); + let now = new Date(); + localStorage.setItem("end_time", (now.setDate(now.getHours() + 12)).toString()); //过期时间 + await getMyUserInfo(userInfo["id"]); + return; + } + }, 2000); + //新标签页打开登录地址 + window.open(loginUrl, "_blank"); + + +}; + //登录接口调用 const onLogin = async () => { console.log("params:", param); diff --git a/src/views/pages/ucenter.vue b/src/views/pages/ucenter.vue index 5afc7f1..d528984 100644 --- a/src/views/pages/ucenter.vue +++ b/src/views/pages/ucenter.vue @@ -71,6 +71,25 @@ + +
+ + + + + 绑定 +
+
+

已绑定的第三方登录账号

+ +
+
@@ -87,14 +106,17 @@ import {GetUserInfoService} from "@/api/user"; import { GetUserStatisticService } from "@/api/user"; import { UploadFileService } from "@/api/tool"; import { UserInfo } from '@/types/user'; +import { ThirdPartyUserInfo } from '@/types/user'; import { FormOption, FormOptionList } from '@/types/form-option'; import { avatarEmits, ElMessage } from 'element-plus'; import TableEdit from '@/components/table-edit.vue'; import {genResetPassword} from "@/api/user"; import {updateUserInfoService} from "@/api/user"; import { useRouter } from "vue-router"; +import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl, getThirdPartyInfo, deleteThirdPartyInfo} from "@/api/user"; const name = localStorage.getItem('ms_username'); +const qqButtonBgImage = ref('https://wiki.connect.qq.com/wp-content/uploads/2016/12/Connect_logo_4.png'); const form = reactive({ new1: '', new: '', @@ -111,12 +133,16 @@ const isUserInfoLoaded = ref(false); const globalData = inject("globalData"); const activeName = ref('label2'); +const activePlatformName = ref('github'); const router = useRouter(); const avatarImg = ref(''); const imgSrc = ref(''); const cropImg = ref(''); const cropper: any = ref(); +const thirdPartyUserInfo = ref([]); + + const reset_password = () => { localStorage.removeItem("ms_username"); router.push('/reset-pwd'); @@ -316,6 +342,116 @@ const saveAvatar =async () => { //更新用户信息 await updateUserInfo(userInfo.value); }; + +const thirdLogin = async (type) => { + //如果该类型的登录已经绑定,则不允许再次绑定 + for (let i = 0; i < thirdPartyUserInfo.value.length; i++) { + if (thirdPartyUserInfo.value[i].third_party_platform === type) { + ElMessage.error("该平台已经绑定,请勿重复绑定!若要绑定其它账号,请先解绑!"); + return; + } + } + //获取uuid + let uuidResp = await getThirdPartyUUID({"type": type}); + if (uuidResp["code"] !== 0) { + ElMessage.error("获取UUID失败!请稍后再试"); + return; + } + let uuid = uuidResp["data"]; + if (!uuid) { + ElMessage.error("获取UUID失败!请稍后再试"); + return; + } + let result={}; + if(type !== "github" && type !== "qq") { + ElMessage.error("不支持的登录平台!请稍后再试:"+type); + return; + } + result = await getThirdPartyLoginUrl({uuid: uuid,"type": "add", "platform": type, "token": localStorage.getItem("token") }); + if (result["code"] !== 0) { + ElMessage.error("获取登录地址失败!请稍后再试"); + return; + } + let loginUrl = result["data"]; + if (!loginUrl) { + ElMessage.error("获取登录地址失败!请稍后再试"); + return; + } + //获取登录状态每2秒请求一次 + let timer = setInterval(async () => { + let statusResp = await getThirdPartyLoginStatus({uuid: uuid}); + if (statusResp["code"] !== 0) { + ElMessage.error("获取登录状态失败!请稍后再试"); + clearInterval(timer); + return; + } + if(statusResp["data"]["status"] === 163) { + ElMessage.error("该账号未绑定,请先绑定账号!"); + clearInterval(timer); + return; + } + let status = statusResp["data"]; + if(status["status"] === 0) { + //登录成功 + clearInterval(timer); + let userInfo = status["user_info"]; + let token = userInfo["token"]; + if (!token) { + ElMessage.error("获取登录状态失败!请稍后再试"); + return; + } + + globalData["token"] = token; + localStorage.setItem("token", token); + localStorage.setItem("userId", userInfo["id"]); + localStorage.setItem("username", status["username"]); + let now = new Date(); + localStorage.setItem("end_time", (now.setDate(now.getHours() + 12)).toString()); //过期时间 + return; + } + }, 2000); + //新标签页打开登录地址 + window.open(loginUrl, "_blank"); +}; + +//获取绑定的第三方账号信息 +const getThirdPartyUserInfo = async () => { + let req = { + token: localStorage.getItem('token'), + }; + try{ + let result = await getThirdPartyInfo(req); + if (result["code"] == 0) { + //绑定的第三方账号信息 + console.log(result.data); + thirdPartyUserInfo.value = result.data; + }else{ + ElMessage.error(result["msg"]); + } + }catch(e){ + console.log(e); + } +}; +getThirdPartyUserInfo(); + +const unBindThirdParty = async (id) => { + let req = { + token: localStorage.getItem('token'), + id: id + }; + try{ + let result = await deleteThirdPartyInfo(req); + if (result["code"] == 0) { + //解绑成功 + ElMessage.success("解绑成功"); + getThirdPartyUserInfo(); + }else{ + ElMessage.error(result["msg"]); + } + }catch(e){ + console.log(e); + } +};