feat: 实现登录注册功能

This commit is contained in:
Frankie Huang 2025-05-03 20:50:34 +08:00
parent 861ff4667c
commit aa058aa888
4 changed files with 277 additions and 4 deletions

7
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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) {

View 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>