不错
This commit is contained in:
parent
61f4176dba
commit
46237d6283
BIN
public/background1.jpg
Normal file
BIN
public/background1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 300 KiB |
BIN
public/background2.jpg
Normal file
BIN
public/background2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
BIN
src/assets/avatar.jpg
Normal file
BIN
src/assets/avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const count = ref(0)
|
const count = ref(0);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -13,7 +13,7 @@ const count = ref(0)
|
|||||||
<img src="../assets/vue.svg" class="logo vue" alt="Vue logo" />
|
<img src="../assets/vue.svg" class="logo vue" alt="Vue logo" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ this.$route.meta.msg }}</h1>
|
<h1>{{ $route.meta.msg }}</h1>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<el-button type="success" @click="count++">count is {{ count }}</el-button>
|
<el-button type="success" @click="count++">count is {{ count }}</el-button>
|
||||||
@ -25,19 +25,24 @@ const count = ref(0)
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
Check out
|
Check out
|
||||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a>, the official Vue + Vite starter
|
||||||
>create-vue</a
|
|
||||||
>, the official Vue + Vite starter
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Learn more about IDE Support for Vue in the
|
Learn more about IDE Support for Vue in the
|
||||||
<a
|
<a href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support" target="_blank">Vue Docs Scaling up Guide</a>.
|
||||||
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
|
||||||
target="_blank"
|
|
||||||
>Vue Docs Scaling up Guide</a
|
|
||||||
>.
|
|
||||||
</p>
|
</p>
|
||||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</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>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,10 +1,23 @@
|
|||||||
// !!!这是一个示例,请根据实际情况修改!!!
|
// authentication.js
|
||||||
// import Login from '../views/authentication/Login.vue'
|
import Login from '../views/authentication/Login.vue';
|
||||||
// import Register from '../views/authentication/Register.vue'
|
import Register from '../views/authentication/Register.vue';
|
||||||
// import Profile from '../views/authentication/Profile.vue'
|
import Profile from '../views/authentication/Profile.vue';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
// { path: '/login', component: Login },
|
{
|
||||||
// { path: '/register', component: Register },
|
path: '/login',
|
||||||
// { path: '/profile', component: Profile }
|
name: 'Login',
|
||||||
]
|
component: Login
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
name: 'Register',
|
||||||
|
component: Register
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/profile',
|
||||||
|
// path: '/profile/:userId',
|
||||||
|
name: 'Profile',
|
||||||
|
component: Profile
|
||||||
|
}
|
||||||
|
];
|
||||||
@ -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();
|
||||||
@ -1,24 +1,25 @@
|
|||||||
const state = {
|
const state = {
|
||||||
user: null
|
token: null
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
setUser(state, user) {
|
setUser(state, {token}) {
|
||||||
state.user = user
|
state.token = token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
login({ commit }, user) {
|
login({commit}, token) {
|
||||||
commit('setUser', user)
|
commit('setUser', token)
|
||||||
},
|
},
|
||||||
logout({ commit }) {
|
logout({commit}) {
|
||||||
commit('setUser', null)
|
commit('setUser', null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
isAuthenticated: state => !!state.user
|
isAuthenticated: state => !!state.token,
|
||||||
|
token: state => state.token
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
73
src/views/authentication/Login.vue
Normal file
73
src/views/authentication/Login.vue
Normal 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>
|
||||||
189
src/views/authentication/Profile.vue
Normal file
189
src/views/authentication/Profile.vue
Normal 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>
|
||||||
84
src/views/authentication/Register.vue
Normal file
84
src/views/authentication/Register.vue
Normal 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>
|
||||||
56
src/views/user-management/Profile.vue
Normal file
56
src/views/user-management/Profile.vue
Normal 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>
|
||||||
Loading…
Reference in New Issue
Block a user