添加第三方登录
This commit is contained in:
parent
bf05991f5d
commit
64126d55d8
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,3 +50,20 @@ export interface UserInfo {
|
|||
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; // 第三方用户主页,可选
|
||||
}
|
||||
|
|
@ -41,7 +41,5 @@ request.interceptors.response.use(
|
|||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
request.interceptors.request.use(
|
||||
|
||||
)
|
||||
export default request;
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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("删除失败");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("删除失败");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue