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-fs": "^2.2.1",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@vavt/v3-extension": "^3.0.0",
|
"@vavt/v3-extension": "^3.0.0",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
"markdown-it-link-attributes": "^4.0.1",
|
"markdown-it-link-attributes": "^4.0.1",
|
||||||
"md-editor-v3": "^5.5.0",
|
"md-editor-v3": "^5.5.0",
|
||||||
"plantuml-encoder": "^1.4.0",
|
"plantuml-encoder": "^1.4.0",
|
||||||
@ -1949,6 +1950,12 @@
|
|||||||
"integrity": "sha512-dAA1/Zbp4+c5E+ARCVTIuKepXsNLzSYfzvOimiYD4S5eeP9QuplSHLcdhfqFSwyM1o1u6ku6RRRCyaZ0YAjiBw==",
|
"integrity": "sha512-dAA1/Zbp4+c5E+ARCVTIuKepXsNLzSYfzvOimiYD4S5eeP9QuplSHLcdhfqFSwyM1o1u6ku6RRRCyaZ0YAjiBw==",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/linkify-it": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
"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-fs": "^2.2.1",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@vavt/v3-extension": "^3.0.0",
|
"@vavt/v3-extension": "^3.0.0",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
"markdown-it-link-attributes": "^4.0.1",
|
"markdown-it-link-attributes": "^4.0.1",
|
||||||
"md-editor-v3": "^5.5.0",
|
"md-editor-v3": "^5.5.0",
|
||||||
"plantuml-encoder": "^1.4.0",
|
"plantuml-encoder": "^1.4.0",
|
||||||
|
|||||||
@ -1,11 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<SelectFolder :rootPath="rootFolderPath" :nodeDataCallback="appendDataForNode" @update:rootPath="changeRootPath"
|
<Space direction="vertical" style="margin-left: 5px; margin-top: 5px;">
|
||||||
@folder-selected="showFileTree" />
|
<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]"
|
<FolderTree ref="folderTreeRef" :treeData="folderTreeData" :expandLevel="1" :selectedNode="[currentFilePath]"
|
||||||
:specifyFileSuffix="['md']" @file-selected="fileSelected" />
|
:specifyFileSuffix="['md']" @file-selected="fileSelected" />
|
||||||
|
</Space>
|
||||||
|
<UserLogin ref="loginRef" v-model:visible="showLoginModal" v-model:loginStatus="loginStatus"></UserLogin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import UserLogin from './UI/UserLogin.vue';
|
||||||
import SelectFolder from './UI/SelectFolder.vue';
|
import SelectFolder from './UI/SelectFolder.vue';
|
||||||
import FolderTree from './UI/FolderTree.vue';
|
import FolderTree from './UI/FolderTree.vue';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
@ -30,6 +39,14 @@ const emit = defineEmits([
|
|||||||
'update:rootFolderPath'
|
'update:rootFolderPath'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 调用登录组件,获取登录状态,以及点击可登出
|
||||||
|
const loginRef = ref(null);
|
||||||
|
const loginStatus = ref(false);
|
||||||
|
const showLoginModal = ref(false);
|
||||||
|
const logout = () => {
|
||||||
|
loginRef.value.logout();
|
||||||
|
};
|
||||||
|
|
||||||
const folderTreeRef = ref(null)
|
const folderTreeRef = ref(null)
|
||||||
// <SelectFolder> 返回的文件树数据,增加自定义数据,便于传给 <FolderTree> 进行渲染
|
// <SelectFolder> 返回的文件树数据,增加自定义数据,便于传给 <FolderTree> 进行渲染
|
||||||
function appendDataForNode(nodeData) {
|
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