This commit is contained in:
Chester.X 2024-07-03 01:19:02 +08:00
parent 61f4176dba
commit 46237d6283
11 changed files with 482 additions and 27 deletions

BIN
public/background1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

BIN
public/background2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
src/assets/avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -1,7 +1,7 @@
<script setup>
import { ref } from 'vue'
import { ref } from 'vue';
const count = ref(0)
const count = ref(0);
</script>
<template>
@ -13,7 +13,7 @@ const count = ref(0)
<img src="../assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<h1>{{ this.$route.meta.msg }}</h1>
<h1>{{ $route.meta.msg }}</h1>
<div class="card">
<el-button type="success" @click="count++">count is {{ count }}</el-button>
@ -25,19 +25,24 @@ const count = ref(0)
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
<a href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support" target="_blank">Vue Docs Scaling up Guide</a>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
<!-- 添加导航链接 -->
<ul>
<li><router-link to="/login">Login</router-link></li>
<li><router-link to="/register">Register</router-link></li>
<li><router-link to="/courses">Courses</router-link></li>
<li><router-link to="/meetings">Meetings</router-link></li>
<li><router-link to="/news">News</router-link></li>
<li><router-link to="/organizations">Organizations</router-link></li>
<li><router-link to="/users">Users</router-link></li>
</ul>
</template>
<style scoped>
@ -56,4 +61,4 @@ const count = ref(0)
.read-the-docs {
color: #888;
}
</style>
</style>

View File

@ -1,10 +1,23 @@
// !!!这是一个示例,请根据实际情况修改!!!
// import Login from '../views/authentication/Login.vue'
// import Register from '../views/authentication/Register.vue'
// import Profile from '../views/authentication/Profile.vue'
// authentication.js
import Login from '../views/authentication/Login.vue';
import Register from '../views/authentication/Register.vue';
import Profile from '../views/authentication/Profile.vue';
export default [
// { path: '/login', component: Login },
// { path: '/register', component: Register },
// { path: '/profile', component: Profile }
]
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
},
{
path: '/profile',
// path: '/profile/:userId',
name: 'Profile',
component: Profile
}
];

View File

@ -0,0 +1,34 @@
// authenticationService.js
import axios from 'axios';
const API_URL = '/api';
class AuthenticationService {
login(credentials) {
return axios.post(`${API_URL}/login`, credentials);
}
register(user) {
return axios.post(`${API_URL}/register`, user);
}
updateUserInfo(user) {
return axios.post(`${API_URL}/update`, user);
}
changePassword(data) {
return axios.post(`${API_URL}/changePassword`, null, {
params: {
username: data.username,
oldPassword: data.oldPassword,
newPassword: data.newPassword
}
});
}
getUserInfo(token) {
return axios.get(`${API_URL}/profile?token=${token}`);
}
}
export default new AuthenticationService();

View File

@ -1,24 +1,25 @@
const state = {
user: null
token: null
}
const mutations = {
setUser(state, user) {
state.user = user
setUser(state, {token}) {
state.token = token
}
}
const actions = {
login({ commit }, user) {
commit('setUser', user)
login({commit}, token) {
commit('setUser', token)
},
logout({ commit }) {
logout({commit}) {
commit('setUser', null)
}
}
const getters = {
isAuthenticated: state => !!state.user
isAuthenticated: state => !!state.token,
token: state => state.token
}
export default {

View File

@ -0,0 +1,73 @@
<script setup>
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage } from 'element-plus';
import AuthenticationService from '../../services/authenticationService';
const router = useRouter();
const store = useStore();
const credentials = reactive({
username: '',
password: ''
});
const login = async () => {
try {
const response = await AuthenticationService.login(credentials);
if (response.status === 200) {
store.commit('authentication/setUser', { token: response.data.token });
}
router.push("/profile");
} catch (error) {
ElMessage.error('Invalid username or password');
console.error("Login error:", error);
}
};
</script>
<template>
<div class="login-wrapper">
<div class="login-container">
<h1>Login</h1>
<el-form @submit.prevent="login" label-width="100px">
<el-form-item label="Username:">
<el-input v-model="credentials.username" id="username"/>
</el-form-item>
<el-form-item label="Password:">
<el-input v-model="credentials.password" id="password" type="password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">Login</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<style scoped>
.login-wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: url('@public/background1.jpg') no-repeat center center;
background-size: cover;
}
.login-container {
max-width: 400px;
width: 100%;
padding: 20px;
border: 1px solid #ebeef5;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
background-color: #fff;
}
h1 {
text-align: center;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,189 @@
<template>
<div class="profile-wrapper">
<div class="profile-container">
<el-tabs v-model="activeTab">
<el-tab-pane label="User Information" name="userInformation"></el-tab-pane>
<el-tab-pane label="Profile" name="profile"></el-tab-pane>
<el-tab-pane label="Change Password" name="changePassword"></el-tab-pane>
</el-tabs>
<div v-if="activeTab === 'userInformation'">
<h2>User Information</h2>
<div class="user-info">
<div class="user-details">
<p>Username: {{ user.username }}</p>
<p>Email: {{ user.email }}</p>
<p>Phone Number: {{ user.phoneNumber }}</p>
<p>Company: {{ user.company }}</p>
<p>Created Date: {{ user.createdDate }}</p>
</div>
<div class="user-avatar">
<img src= '@assets/avatar.jpg' alt="User Avatar" />
</div>
</div>
</div>
<div v-if="activeTab === 'profile'">
<h1>Profile</h1>
<el-form @submit.prevent="updateProfile" label-width="120px" class="profile-form">
<el-form-item label="Username:">
<el-input v-model="user.username" id="username" disabled />
</el-form-item>
<el-form-item label="Email:">
<el-input v-model="user.email" id="email" />
</el-form-item>
<el-form-item label="Phone Number:">
<el-input v-model="user.phoneNumber" id="phoneNumber" />
</el-form-item>
<el-form-item label="Company:">
<el-input v-model="user.company" id="company" />
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">Update Profile</el-button>
</el-form-item>
</el-form>
</div>
<div v-if="activeTab === 'changePassword'">
<h2>Change Password</h2>
<el-form @submit.prevent="changePassword" label-width="120px" class="password-form">
<el-form-item label="Old Password:">
<el-input v-model="passwords.oldPassword" id="oldPassword" type="password" />
</el-form-item>
<el-form-item label="New Password:">
<el-input v-model="passwords.newPassword" id="newPassword" type="password" />
</el-form-item>
<el-form-item label="Confirm Password:">
<el-input v-model="passwords.confirmPassword" id="confirmPassword" type="password" />
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">Change Password</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
import { ElMessage } from 'element-plus';
import AuthenticationService from '../../services/authenticationService';
const store = useStore();
const activeTab = ref('userInformation');
const user = ref({
id: null,
username: '',
email: '',
phoneNumber: '',
company: '',
createdDate: ''
});
const passwords = ref({
oldPassword: '',
newPassword: '',
confirmPassword: ''
});
const token = store.getters['authentication/token'];
const fetchUserInfo = async () => {
try {
const response = await AuthenticationService.getUserInfo(token);
user.value = response.data;
} catch (error) {
console.error("Error fetching user info:", error);
}
};
const updateProfile = async () => {
try {
await AuthenticationService.updateUserInfo(user.value);
ElMessage.success('Profile updated successfully');
await fetchUserInfo();
} catch (error) {
console.error("Error updating profile:", error);
ElMessage.error('Error updating profile');
}
};
const changePassword = async () => {
if (passwords.value.newPassword !== passwords.value.confirmPassword) {
ElMessage.error('New password and confirm password do not match');
return;
}
try {
await AuthenticationService.changePassword({
username: user.value.username,
oldPassword: passwords.value.oldPassword,
newPassword: passwords.value.newPassword
});
ElMessage.success('Password changed successfully');
} catch (error) {
console.error("Error changing password:", error);
ElMessage.error('Old password is incorrect');
}
};
onMounted(() => {
fetchUserInfo();
});
</script>
<style scoped>
.profile-wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-image: url('@public/background2.jpg');
background-size: cover;
background-position: center;
}
.profile-container {
max-width: 600px;
width: 100%;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h1, h2 {
text-align: center;
margin-bottom: 20px;
}
.profile-form, .password-form {
margin-bottom: 30px;
}
.user-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-top: 1px solid #ebeef5;
margin-top: 20px;
}
.user-details {
flex: 1;
}
.user-avatar {
margin-left: 20px;
}
.user-avatar img {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
</style>

View File

@ -0,0 +1,84 @@
<template>
<div>
<h2>Register</h2>
<form @submit.prevent="register">
<div>
<label for="username">Username:</label>
<input type="text" v-model="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" v-model="password" required>
</div>
<div>
<label for="email">Email:</label>
<input type="email" v-model="email" required>
</div>
<div>
<label for="phoneNumber">Phone Number:</label>
<input type="text" v-model="phoneNumber">
</div>
<div>
<label for="company">Company:</label>
<input type="text" v-model="company">
</div>
<div>
<label for="role">Role:</label>
<select v-model="role">
<option value="USER">User</option>
<option value="ADMIN">Admin</option>
</select>
</div>
<div>
<label for="verificationCode">Verification Code:</label>
<input type="text" v-model="verificationCode" required>
<button type="button" @click="getVerificationCode">Get Verification Code</button>
</div>
<button type="submit">Register</button>
</form>
<p v-if="message">{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import authenticationService from '../../services/authenticationService';
const username = ref('');
const password = ref('');
const email = ref('');
const phoneNumber = ref('');
const company = ref('');
const role = ref('USER');
const verificationCode = ref('');
const message = ref('');
const router = useRouter();
const getVerificationCode = async () => {
//
message.value = 'Verification code sent. Please check your phone/email.';
};
const register = async () => {
if (!verificationCode.value) {
message.value = 'Please enter the verification code.';
return;
}
try {
const response = await authenticationService.register({
username: username.value,
password: password.value,
email: email.value,
phoneNumber: phoneNumber.value,
company: company.value,
role: role.value
});
message.value = response.data.message;
router.push('/login');
} catch (error) {
console.error("Registration failed:", error.response.data.message);
message.value = error.response.data.message;
}
};
</script>

View File

@ -0,0 +1,56 @@
<template>
<div>
<h2>Profile</h2>
<form @submit.prevent="updateProfile">
<div>
<label for="nickname">Nickname:</label>
<input type="text" v-model="user.nickname">
</div>
<div>
<label for="phoneNumber">Phone Number:</label>
<input type="text" v-model="user.phoneNumber">
</div>
<div>
<label for="gender">Gender:</label>
<input type="text" v-model="user.gender">
</div>
<button type="submit">Update Profile</button>
</form>
<form @submit.prevent="changePassword">
<div>
<label for="oldPassword">Old Password:</label>
<input type="password" v-model="oldPassword" required>
</div>
<div>
<label for="newPassword">New Password:</label>
<input type="password" v-model="newPassword" required>
</div>
<button type="submit">Change Password</button>
</form>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
data() {
return {
oldPassword: '',
newPassword: ''
};
},
computed: {
...mapState(['user'])
},
methods: {
...mapActions(['updateProfile', 'changePassword']),
async updateProfile() {
await this.updateProfile(this.user);
},
async changePassword() {
await this.changePassword({ oldPassword: this.oldPassword, newPassword: this.newPassword });
}
}
};
</script>