feat: 实现登录注册功能
This commit is contained in:
parent
861ff4667c
commit
aa058aa888
7
package-lock.json
generated
7
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
@ -1949,6 +1950,12 @@
|
||||
"integrity": "sha512-dAA1/Zbp4+c5E+ARCVTIuKepXsNLzSYfzvOimiYD4S5eeP9QuplSHLcdhfqFSwyM1o1u6ku6RRRCyaZ0YAjiBw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-md5": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
|
||||
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
<template>
|
||||
<SelectFolder :rootPath="rootFolderPath" :nodeDataCallback="appendDataForNode" @update:rootPath="changeRootPath"
|
||||
@folder-selected="showFileTree" />
|
||||
<FolderTree ref="folderTreeRef" :treeData="folderTreeData" :expandLevel="1" :selectedNode="[currentFilePath]"
|
||||
:specifyFileSuffix="['md']" @file-selected="fileSelected" />
|
||||
<Space direction="vertical" style="margin-left: 5px; margin-top: 5px;">
|
||||
<Space>
|
||||
<Button v-if="!loginStatus" type="primary" shape="circle" icon="md-person"
|
||||
@click="showLoginModal = true"></Button>
|
||||
<Button v-else type="error" shape="circle" icon="md-log-out" @click="logout"></Button>
|
||||
<SelectFolder :rootPath="rootFolderPath" :nodeDataCallback="appendDataForNode"
|
||||
@update:rootPath="changeRootPath" @folder-selected="showFileTree" />
|
||||
</Space>
|
||||
<FolderTree ref="folderTreeRef" :treeData="folderTreeData" :expandLevel="1" :selectedNode="[currentFilePath]"
|
||||
:specifyFileSuffix="['md']" @file-selected="fileSelected" />
|
||||
</Space>
|
||||
<UserLogin ref="loginRef" v-model:visible="showLoginModal" v-model:loginStatus="loginStatus"></UserLogin>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import UserLogin from './UI/UserLogin.vue';
|
||||
import SelectFolder from './UI/SelectFolder.vue';
|
||||
import FolderTree from './UI/FolderTree.vue';
|
||||
import { ref } from 'vue';
|
||||
@ -30,6 +39,14 @@ const emit = defineEmits([
|
||||
'update:rootFolderPath'
|
||||
]);
|
||||
|
||||
// 调用登录组件,获取登录状态,以及点击可登出
|
||||
const loginRef = ref(null);
|
||||
const loginStatus = ref(false);
|
||||
const showLoginModal = ref(false);
|
||||
const logout = () => {
|
||||
loginRef.value.logout();
|
||||
};
|
||||
|
||||
const folderTreeRef = ref(null)
|
||||
// <SelectFolder> 返回的文件树数据,增加自定义数据,便于传给 <FolderTree> 进行渲染
|
||||
function appendDataForNode(nodeData) {
|
||||
|
||||
248
src/components/UI/UserLogin.vue
Normal file
248
src/components/UI/UserLogin.vue
Normal file
@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<Modal v-model="showFormModal" :footer-hide="true" class="login-modal">
|
||||
<div style="padding-top: 30px;"></div>
|
||||
<Login v-if="signInOrSignUp" @on-submit="handleSignIn">
|
||||
<UserName name="username" />
|
||||
<Password name="password" />
|
||||
<div class="auto-login">
|
||||
<Checkbox v-model="autoLogin" size="large">记住登录状态</Checkbox>
|
||||
<!-- <a>忘记密码</a> -->
|
||||
</div>
|
||||
<Submit />
|
||||
<Space split>还未拥有账号?<Button type="text" @click="signInOrSignUp = false">前往注册</Button></Space>
|
||||
</Login>
|
||||
<Login v-else @on-submit="handleSignUp">
|
||||
<UserName name="username" />
|
||||
<Email name="mail" />
|
||||
<Password name="password" />
|
||||
<Password name="passwordConfirm" placeholder="确认密码" />
|
||||
<Submit>注册</Submit>
|
||||
<Space split>已经拥有账号?<Button type="text" @click="signInOrSignUp = true">前往登录</Button></Space>
|
||||
</Login>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import md5 from 'js-md5';
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { Message } from 'view-ui-plus'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
loginStatus: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:visible',
|
||||
'update:loginStatus',
|
||||
]);
|
||||
|
||||
// 是否弹出对话框。要求双向绑定
|
||||
const showFormModal = ref(props.visible);
|
||||
watch(() => props.visible, (visible) => {
|
||||
showFormModal.value = visible;
|
||||
})
|
||||
watch(showFormModal, (visible) => {
|
||||
emit('update:visible', visible);
|
||||
})
|
||||
|
||||
const signInOrSignUp = ref(true);
|
||||
const autoLogin = ref(false);
|
||||
|
||||
// 由于是跨域登录,需要手动设置并携带 Cookie
|
||||
const setPHPSessionToCookie = (sessionID) => {
|
||||
if (sessionID) {
|
||||
document.cookie = `PHPSESSID=${sessionID}; max-age=604800; SameSite=None; secure; path=/`;
|
||||
}
|
||||
}
|
||||
const getPHPSessionFromCookie = () => {
|
||||
let PHPSESSID = '';
|
||||
const cookies = document.cookie.split('; ');
|
||||
cookies.forEach(cookie => {
|
||||
const [key, value] = cookie.split('=');
|
||||
if (key === 'PHPSESSID') {
|
||||
PHPSESSID = decodeURIComponent(value); // 解码特殊字符(如空格、%20)
|
||||
}
|
||||
});
|
||||
return PHPSESSID;
|
||||
}
|
||||
const delPHPSessionFromCookie = () => {
|
||||
document.cookie = `PHPSESSID=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
||||
}
|
||||
|
||||
const handleSignIn = (valid, { username, password }) => {
|
||||
if (valid) {
|
||||
fetch('https://myafei.cn/php/ajax.php', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
'func': 'login',
|
||||
'username': username,
|
||||
'is_email': /^[0-9a-zA-Z_-]{1,100}$/.test(username) ? false : true,
|
||||
'password': md5(password),
|
||||
'isRemember': autoLogin.value,
|
||||
}),
|
||||
}).then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP 错误: ${response.status}`);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.status == 0) {
|
||||
Message.success('登录成功');
|
||||
setPHPSessionToCookie(data.PHPSESSID);
|
||||
// 将登录成功的状态通知父组件
|
||||
emit('update:loginStatus', true);
|
||||
// 关闭对话框
|
||||
showFormModal.value = false;
|
||||
} else {
|
||||
Message.error(data.error);
|
||||
}
|
||||
}).catch(error => {
|
||||
Message.error(`登录发生异常: ${error}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSignUp = (valid, { username, mail, password, passwordConfirm }) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
if (passwordConfirm !== password) {
|
||||
Message.error('输入的密码不一致,请重新输入');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('https://myafei.cn/php/ajax.php', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
'func': 'register',
|
||||
'username': username,
|
||||
'email': mail,
|
||||
'password': md5(password),
|
||||
}),
|
||||
}).then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP 错误: ${response.status}`);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.status == 0) {
|
||||
// 发送邮件
|
||||
sendSignUpEmail(data.id);
|
||||
} else {
|
||||
Message.error(data.error);
|
||||
}
|
||||
}).catch(error => {
|
||||
Message.error(`注册发生异常: ${error}`);
|
||||
});
|
||||
};
|
||||
|
||||
const sendSignUpEmail = (userID) => {
|
||||
fetch('https://myafei.cn/php/ajax.php', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
'func': 'resend_email',
|
||||
'id': userID,
|
||||
'path': '/',
|
||||
}),
|
||||
}).then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP 错误: ${response.status}`);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.status == 0) {
|
||||
Message.success('请前往邮箱激活账号(没收到的话检查垃圾箱),激活后再回来登录账号');
|
||||
// 回到登录窗口
|
||||
signInOrSignUp.value = true;
|
||||
} else if (data.status == -100) {
|
||||
Message.warning('邮箱已被激活');
|
||||
} else {
|
||||
Message.error('激活邮件发送失败:' + data.error);
|
||||
}
|
||||
}).catch(error => {
|
||||
Message.error(`邮件发送异常: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
const checkLoginStatus = async () => {
|
||||
// 先从 cookie 中拿到 PHPSESSID
|
||||
const PHPSESSID = getPHPSessionFromCookie();
|
||||
let loginStatus = false;
|
||||
await fetch('https://myafei.cn/php/ajax.php?PHPSESSID=' + PHPSESSID, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
'func': 'is_login',
|
||||
}),
|
||||
}).then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP 错误: ${response.status}`);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.status == 0) {
|
||||
emit('update:loginStatus', true);
|
||||
loginStatus = true;
|
||||
} else {
|
||||
loginStatus = false;
|
||||
}
|
||||
}).catch(error => {
|
||||
Message.error(`检查登录状态时发生异常: ${error}`);
|
||||
loginStatus = false;
|
||||
});
|
||||
return loginStatus;
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
const PHPSESSID = getPHPSessionFromCookie();
|
||||
let logoutStatus = false;
|
||||
await fetch('https://myafei.cn/php/ajax.php?PHPSESSID=' + PHPSESSID, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
'func': 'logout',
|
||||
}),
|
||||
}).then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP 错误: ${response.status}`);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.status == 0) {
|
||||
Message.info('登出成功');
|
||||
delPHPSessionFromCookie();
|
||||
emit('update:loginStatus', false);
|
||||
logoutStatus = true;
|
||||
} else {
|
||||
Message.error(data.error);
|
||||
logoutStatus = false;
|
||||
}
|
||||
}).catch(error => {
|
||||
Message.error(`登出发生异常: ${error}`);
|
||||
logoutStatus = false;
|
||||
});
|
||||
return logoutStatus;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
logout,
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// 检查是否已登录
|
||||
checkLoginStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-modal {
|
||||
width: 400px;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.auto-login {
|
||||
margin-bottom: 24px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.auto-login a {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user