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 }),
|
api.post('/userMgr/remove', { targetUserID }),
|
||||||
listUsers: () =>
|
listUsers: () =>
|
||||||
api.get('/userMgr/list'),
|
api.get('/userMgr/list'),
|
||||||
|
modifyUser: (userID, username, passwd) =>
|
||||||
|
api.post(`/userMgr/modify`, { userID, username, passwd }),
|
||||||
modifyType: (userID, newType) =>
|
modifyType: (userID, newType) =>
|
||||||
api.post(`/userMgr/modifyType`, { userID, newType: newType.toLowerCase() || 'visitor' })
|
api.post(`/userMgr/modifyType`, { userID, newType: newType.toLowerCase() || 'visitor' })
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<span class="username">{{ username }}</span>
|
<span class="username">{{ username }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-menu" v-show="menuVisible" @click.stop>
|
<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>
|
<i class="fas fa-user-cog"></i>
|
||||||
<span>User Settings</span>
|
<span>User Settings</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -38,14 +38,49 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||||
import { systemApi, authApi } from '../api/index.js';
|
import { systemApi, authApi, userApi } from '../api/index.js';
|
||||||
import { getCookie, setCookie, deleteCookie as removeCookie } from '../utils/functions.js';
|
import { getCookie, setCookie, deleteCookie as removeCookie, showNotification } from '../utils/functions.js';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -57,7 +92,15 @@ export default {
|
|||||||
const softwareInfo = ref(null);
|
const softwareInfo = ref(null);
|
||||||
const username = ref(getCookie('username') || 'User');
|
const username = ref(getCookie('username') || 'User');
|
||||||
const menuVisible = ref(false);
|
const menuVisible = ref(false);
|
||||||
|
const showEditUser = ref(false);
|
||||||
const userInfoRef = ref(null);
|
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 router = useRouter();
|
||||||
|
|
||||||
const checkStatus = async () => {
|
const checkStatus = async () => {
|
||||||
@@ -86,9 +129,61 @@ export default {
|
|||||||
menuVisible.value = !menuVisible.value;
|
menuVisible.value = !menuVisible.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSettings = () => {
|
const handleEditUser = () => {
|
||||||
menuVisible.value = false;
|
showEditUser.value = true;
|
||||||
alert('User settings feature coming soon!');
|
};
|
||||||
|
|
||||||
|
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 () => {
|
const handleLogout = async () => {
|
||||||
@@ -130,9 +225,14 @@ export default {
|
|||||||
handleSearch,
|
handleSearch,
|
||||||
menuVisible,
|
menuVisible,
|
||||||
toggleMenu,
|
toggleMenu,
|
||||||
handleSettings,
|
handleEditUser,
|
||||||
|
showEditUser,
|
||||||
|
handleEditUserSubmit,
|
||||||
|
closeEditUserDialog,
|
||||||
handleLogout,
|
handleLogout,
|
||||||
userInfoRef
|
userInfoRef,
|
||||||
|
loading,
|
||||||
|
editFormData
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -319,4 +419,101 @@ export default {
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
text-align: center;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user