feat(user-settings): add user settings modal with username and password update
Implement user settings functionality in TopBar component with a modal dialog that allows users to update their username and password. The modal includes form validation and error handling, with proper password confirmation checks.
This commit is contained in:
@@ -52,6 +52,8 @@ export const userApi = {
|
||||
api.post('/userMgr/remove', { targetUserID }),
|
||||
listUsers: () =>
|
||||
api.get('/userMgr/list'),
|
||||
modifyUser: (userID, username, passwd) =>
|
||||
api.post(`/userMgr/modify`, { userID, username, passwd }),
|
||||
modifyType: (userID, newType) =>
|
||||
api.post(`/userMgr/modifyType`, { userID, newType: newType.toLowerCase() || 'visitor' })
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<span class="username">{{ username }}</span>
|
||||
</div>
|
||||
<div class="user-menu" v-show="menuVisible" @click.stop>
|
||||
<button class="menu-item" @click="handleSettings">
|
||||
<button class="menu-item" @click="showEditUser = true">
|
||||
<i class="fas fa-user-cog"></i>
|
||||
<span>User Settings</span>
|
||||
</button>
|
||||
@@ -38,14 +38,49 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showEditUser" class="modal-overlay" @click="closeEditUserDialog">
|
||||
<div class="modal" @click.stop>
|
||||
<h3>User Settings</h3>
|
||||
<form @submit.prevent="handleEditUserSubmit" class="form">
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="editFormData.newUsername"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<input
|
||||
type="password"
|
||||
v-model="editFormData.newPasswd"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
v-model="editFormData.confirmPasswd"
|
||||
>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="cancel-btn" @click="closeEditUserDialog">Cancel</button>
|
||||
<button type="submit" class="submit-btn" :disabled="loading">
|
||||
{{ loading ? 'Processing...' : 'Submit' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { systemApi, authApi } from '../api/index.js';
|
||||
import { getCookie, setCookie, deleteCookie as removeCookie } from '../utils/functions.js';
|
||||
import { systemApi, authApi, userApi } from '../api/index.js';
|
||||
import { getCookie, setCookie, deleteCookie as removeCookie, showNotification } from '../utils/functions.js';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export default {
|
||||
@@ -57,7 +92,15 @@ export default {
|
||||
const softwareInfo = ref(null);
|
||||
const username = ref(getCookie('username') || 'User');
|
||||
const menuVisible = ref(false);
|
||||
const showEditUser = ref(false);
|
||||
const userInfoRef = ref(null);
|
||||
const loading = ref(false);
|
||||
const userID = ref(parseInt(getCookie('user-id') || '0'));
|
||||
const editFormData = ref({
|
||||
newUsername: '',
|
||||
newPasswd: '',
|
||||
confirmPasswd: ''
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const checkStatus = async () => {
|
||||
@@ -86,9 +129,61 @@ export default {
|
||||
menuVisible.value = !menuVisible.value;
|
||||
};
|
||||
|
||||
const handleSettings = () => {
|
||||
menuVisible.value = false;
|
||||
alert('User settings feature coming soon!');
|
||||
const handleEditUser = () => {
|
||||
showEditUser.value = true;
|
||||
};
|
||||
|
||||
const handleEditUserSubmit = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const userIDValue = userID.value;
|
||||
const newUsername = editFormData.value.newUsername;
|
||||
const newPasswd = editFormData.value.newPasswd;
|
||||
const confirmPasswd = editFormData.value.confirmPasswd;
|
||||
|
||||
if (!newUsername.trim()) {
|
||||
showNotification('Username cannot be empty', 'error');
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPasswd || confirmPasswd) {
|
||||
if (newPasswd !== confirmPasswd) {
|
||||
showNotification('Passwords do not match', 'error');
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let response;
|
||||
if (newPasswd && newPasswd === confirmPasswd) {
|
||||
response = await userApi.modifyUser(userIDValue, newUsername, newPasswd);
|
||||
} else {
|
||||
response = await userApi.modifyUser(userIDValue, newUsername, '');
|
||||
}
|
||||
|
||||
if (response.success) {
|
||||
showNotification('User information updated successfully', 'success');
|
||||
username.value = newUsername;
|
||||
setCookie('username', newUsername);
|
||||
closeEditUserDialog();
|
||||
} else {
|
||||
showNotification(response.message || 'Failed to update user', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification(error.message || 'Failed to update user', 'error');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const closeEditUserDialog = () => {
|
||||
showEditUser.value = false;
|
||||
editFormData.value = {
|
||||
newUsername: '',
|
||||
newPasswd: '',
|
||||
confirmPasswd: ''
|
||||
};
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
@@ -130,9 +225,14 @@ export default {
|
||||
handleSearch,
|
||||
menuVisible,
|
||||
toggleMenu,
|
||||
handleSettings,
|
||||
handleEditUser,
|
||||
showEditUser,
|
||||
handleEditUserSubmit,
|
||||
closeEditUserDialog,
|
||||
handleLogout,
|
||||
userInfoRef
|
||||
userInfoRef,
|
||||
loading,
|
||||
editFormData
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -319,4 +419,101 @@ export default {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.modal h3 {
|
||||
font-size: 20px;
|
||||
color: var(--text-color);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: var(--hover-bg);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.submit-btn:hover:not(:disabled) {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user