添加第三方登录

This commit is contained in:
junleea 2025-04-27 13:09:57 +08:00
parent bf05991f5d
commit 64126d55d8
9 changed files with 369 additions and 11 deletions

View File

@ -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": [

View File

@ -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;
};

View File

@ -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; // 第三方用户主页,可选
}

View File

@ -41,7 +41,5 @@ request.interceptors.response.use(
return Promise.reject(error);
}
)
request.interceptors.request.use(
)
export default request;

View File

@ -51,6 +51,24 @@
>
</p>
</el-form>
<div>
<!-- 第三登录 -->
<el-divider content-position="center">第三方登录</el-divider>
<div class="login-text">
<el-button
class="login-btn"
type="primary"
icon="el-icon-github"
@click="thirdLogin('github')"
>GitHub</el-button>
<el-button
class="login-btn"
type="primary"
:style="{ backgroundImage: `url('${qqButtonBgImage}')` }"
@click="thirdLogin('qq')"
></el-button>
</div>
</div>
</div>
</div>
</template>
@ -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);

View File

@ -71,6 +71,25 @@
<el-tab-pane name="label4" v-if="isUserInfoLoaded" label="详细信息" class="user-tabpane">
<TableEdit :form-data="userInfo" :options="options_edit" :edit="true" :update="updateUserInfo" />
</el-tab-pane>
<el-tab-pane name="label5" label="第三方账号" class="user-tabpane">
<div>
<el-select v-model="activePlatformName" placeholder="请选择第三方平台" size="small" style="width: 200px;">
<el-option label="QQ" value="qq"></el-option>
<el-option label="Github" value="github"></el-option>
</el-select>
<el-button type="primary" @click="thirdLogin(activePlatformName)">绑定</el-button>
</div>
<div>
<p>已绑定的第三方登录账号</p>
<template v-for="(item, index) in thirdPartyUserInfo" :key="index">
<el-tag>{{ item.third_party_platform }}</el-tag>
<!-- 头像 -->
<el-avatar :src="item.third_party_user_avatar" size="small"></el-avatar>
<el-tag>{{ item.third_party_user_name }}</el-tag>
<el-button type="danger" @click="unBindThirdParty(item.ID)">解绑</el-button>
</template>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
@ -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<ThirdPartyUserInfo[]>([]);
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);
}
};
</script>
<style scoped>
@ -438,6 +574,11 @@ const saveAvatar =async () => {
.user-footer > div + div {
border-left: 1px solid rgba(83, 70, 134, 0.1);
}
.login-btn {
display: block;
width: 100%;
}
</style>
<style>

View File

@ -351,6 +351,10 @@ const searchFileQuery = ref(""); // 用于搜索文件的查询条件
const filteredFiles = ref<File[]>([]); //
const uploadFileVisible = ref(false); //
const historyMsgHtml= ref([]); // HTML
const wssUrl =
"wss://pm.ljsea.top/im/ai_chat_ws?" +
"token=" +
localStorage.getItem("token");
const renderMarkdown = (message: Message, index:number) => {
if(message.finished == false){
@ -416,6 +420,7 @@ const handleSelectFileConfirm = () => {
selectFileVisible.value = false; //
};
const doButtonD = () => {
const codeBlocks = document.querySelectorAll("pre code");
codeBlocks.forEach((codeBlock) => {
@ -517,11 +522,13 @@ onMounted(() => {
console.log("WebSocket 连接已关闭");
ElMessage.error("连接已关闭");
//
socket.value = new WebSocket(url);
//socket.value = new WebSocket(url);
socket.value = null;
};
socket.value.onerror = (error) => {
socket.value = new WebSocket(url);
//socket.value = new WebSocket(url);
socket.value = null;
console.error("WebSocket 发生错误:", error);
};
});
@ -532,6 +539,37 @@ onUnmounted(() => {
}
});
const doReceiveMessage = (event) => {
//
let msg: WSMessage = JSON.parse(event.data);
const existingMessage = messages.find(
(msg) => msg.role === "assistant" && !msg.finished
);
if (existingMessage) {
//
existingMessage.content += msg.msg.msg.response;
} else {
//
messages.push({
role: "assistant",
content: msg.msg.msg.response,
finished: false,
});
}
//console.log("resp:", msg);
sessionID.value = msg.session_id;
currentAIMessage.value += msg.msg.msg.response;
if (msg.msg.msg.done) {
const assistantMessage = messages[messages.length - 1];
assistantMessage.finished = true;
loading.value = false;
doButtonD();
}
nextTick(() => {
scrollToBottom(); //
});
}
const sendMessage = () => {
sendMessageWithFile()
return;
@ -641,11 +679,32 @@ const sendMessageWithFile = () => {
}
console.log("end_msg:", end_msg);
if(socket.value == null){
socket.value = new WebSocket(wssUrl);
socket.value.onmessage = (event) => {
doReceiveMessage(event);
};
socket.value.onclose = () => {
console.log("WebSocket 连接已关闭");
ElMessage.error("连接已关闭");
//
//socket.value = new WebSocket(url);
socket.value = null;
};
socket.value.onerror = (error) => {
//socket.value = new WebSocket(url);
socket.value = null;
console.error("WebSocket 发生错误:", error);
};
ElMessage.error("已重新连接!");
}
try {
socket.value.send(JSON.stringify(end_msg));
console.log("send msg:", end_msg);
} catch (e) {
ElMessage.error("发送失败!连接已断开!");
socket.value = new WebSocket(socket.value.url);
return;
}
if (sessionID.value == 0) {

View File

@ -235,13 +235,13 @@ const downloadFile = async (row: File) => {
const handleDelete = async (row: File) => {
let req={
token: localStorage.getItem('token'),
id: row.ID,
file_id: row.ID,
}
try{
let result = await DelUserFileService(req);
if(result["code"] === 0){
ElMessage.success("删除成功");
handleSearch();
getData();
}else{
ElMessage.error("删除失败");
}

View File

@ -219,7 +219,7 @@ const handleDelete = async (row: Session) => {
let result = await DelSessionService(req);
if(result["code"] === 0){
ElMessage.success("删除成功");
handleSearch();
getData();
}else{
ElMessage.error("删除失败");
}