645 lines
22 KiB
JavaScript
645 lines
22 KiB
JavaScript
class UserManagement extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: 'open' });
|
|
this.users = [];
|
|
this.currentUser = null;
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
this.fetchUsers();
|
|
}
|
|
|
|
async fetchUsers() {
|
|
try {
|
|
const response = await fetch('/api/all-users');
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
this.users = data.users.filter(user => user.id !== 1);
|
|
this.render();
|
|
} else {
|
|
console.error('获取用户列表失败:', data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取用户列表出错:', error);
|
|
}
|
|
}
|
|
|
|
showError(message) {
|
|
this.shadowRoot.querySelector('.error-message').textContent = message;
|
|
this.shadowRoot.querySelector('.error-message').style.display = 'block';
|
|
}
|
|
|
|
async handleEditInfo(userId) {
|
|
try {
|
|
const response = await fetch(`/api/user-info/${userId}`);
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
this.currentUser = data.user;
|
|
this.showEditModal();
|
|
} else {
|
|
alert('获取用户信息失败: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取用户信息出错:', error);
|
|
alert('获取用户信息失败');
|
|
}
|
|
}
|
|
|
|
showEditModal() {
|
|
this.shadowRoot.querySelector('.modal').style.display = 'flex';
|
|
this.populateForm();
|
|
}
|
|
|
|
hideEditModal() {
|
|
this.shadowRoot.querySelector('.modal').style.display = 'none';
|
|
this.currentUser = null;
|
|
}
|
|
|
|
populateForm() {
|
|
const form = this.shadowRoot.querySelector('.edit-form');
|
|
form.username.value = this.currentUser.username || '';
|
|
form.full_name.value = this.currentUser.full_name || '';
|
|
form.email.value = this.currentUser.email || '';
|
|
form.phone.value = this.currentUser.phone || '';
|
|
form.department.value = this.currentUser.department || '';
|
|
form.position.value = this.currentUser.position || '';
|
|
}
|
|
|
|
async handleSaveUserInfo() {
|
|
const form = this.shadowRoot.querySelector('.edit-form');
|
|
const userInfo = {
|
|
full_name: form.full_name.value,
|
|
email: form.email.value,
|
|
phone: form.phone.value,
|
|
department: form.department.value,
|
|
position: form.position.value
|
|
};
|
|
|
|
try {
|
|
const response = await fetch('/api/update-user-info', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
userId: this.currentUser.id,
|
|
userInfo: userInfo
|
|
})
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
alert('用户信息更新成功');
|
|
this.hideEditModal();
|
|
this.fetchUsers(); // 刷新用户列表
|
|
} else {
|
|
alert('用户信息更新失败: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('更新用户信息出错:', error);
|
|
alert('更新用户信息失败');
|
|
}
|
|
}
|
|
|
|
handleChangeAccessLevel(userId) {
|
|
this.currentUser = { id: userId };
|
|
this.showAccessModal();
|
|
}
|
|
|
|
showAccessModal() {
|
|
this.shadowRoot.querySelector('.access-modal').style.display = 'flex';
|
|
}
|
|
|
|
hideAccessModal() {
|
|
this.shadowRoot.querySelector('.access-modal').style.display = 'none';
|
|
this.currentUser = null;
|
|
}
|
|
|
|
async handleSaveAccessLevel() {
|
|
const form = this.shadowRoot.querySelector('.access-form');
|
|
const accessLevel = parseInt(form.access_level.value);
|
|
|
|
try {
|
|
const response = await fetch('/api/update-access-level', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
userId: this.currentUser.id,
|
|
accessLevel: accessLevel
|
|
})
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
alert('权限级别更新成功');
|
|
this.hideAccessModal();
|
|
this.fetchUsers(); // 刷新用户列表
|
|
} else {
|
|
alert('权限级别更新失败: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('更新权限级别出错:', error);
|
|
alert('更新权限级别失败');
|
|
}
|
|
}
|
|
|
|
async handleResetPassword(userId) {
|
|
if (confirm('确定要重置该用户的密码吗?')) {
|
|
try {
|
|
const response = await fetch('/api/reset-password', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ userId: parseInt(userId) })
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
alert('密码重置成功');
|
|
} else {
|
|
alert('密码重置失败: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('重置密码出错:', error);
|
|
alert('重置密码失败');
|
|
}
|
|
}
|
|
}
|
|
|
|
async handleDelete(userId) {
|
|
if (confirm('确定要删除该用户吗?此操作不可恢复!')) {
|
|
try {
|
|
const response = await fetch('/api/delete-user', {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ userId: parseInt(userId) })
|
|
});
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
alert('用户删除成功');
|
|
// 重新获取用户列表
|
|
this.fetchUsers();
|
|
} else {
|
|
alert('用户删除失败: ' + data.message);
|
|
}
|
|
} catch (error) {
|
|
console.error('删除用户出错:', error);
|
|
alert('删除用户失败');
|
|
}
|
|
}
|
|
}
|
|
|
|
getAccessLevelName(level) {
|
|
const accessLevels = {
|
|
0: '访客',
|
|
1: '用户',
|
|
2: '开发人员',
|
|
3: '组长'
|
|
};
|
|
return accessLevels[level] || level;
|
|
}
|
|
|
|
render() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
height: 100%;
|
|
overflow: auto;
|
|
padding: 16px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.users-container {
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
padding: 16px;
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.users-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.users-title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.users-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
}
|
|
|
|
.users-table th,
|
|
.users-table td {
|
|
padding: 12px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.users-table th {
|
|
background-color: #f5f5f5;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.users-table tr:hover {
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.col-id {
|
|
width: 8%;
|
|
}
|
|
|
|
.col-username {
|
|
width: 20%;
|
|
}
|
|
|
|
.col-fullname {
|
|
width: 25%;
|
|
}
|
|
|
|
.col-access {
|
|
width: 12%;
|
|
}
|
|
|
|
.col-actions {
|
|
width: 35%;
|
|
}
|
|
|
|
.error-message {
|
|
display: none;
|
|
color: #ff4d4f;
|
|
margin: 16px 0;
|
|
padding: 8px;
|
|
background-color: #fff2f0;
|
|
border: 1px solid #ffccc7;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #666;
|
|
}
|
|
|
|
.action-button {
|
|
padding: 6px 12px;
|
|
margin: 0 4px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
color: white;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.edit-info {
|
|
background-color: #1890ff;
|
|
}
|
|
|
|
.change-access {
|
|
background-color: #52c41a;
|
|
}
|
|
|
|
.reset-password {
|
|
background-color: #faad14;
|
|
}
|
|
|
|
.delete {
|
|
background-color: #ff4d4f;
|
|
}
|
|
|
|
.action-button:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.action-button:active {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.modal-content {
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
padding: 24px;
|
|
width: 500px;
|
|
max-width: 90%;
|
|
max-height: 90%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.close-button {
|
|
background: none;
|
|
border: none;
|
|
font-size: 20px;
|
|
cursor: pointer;
|
|
color: #999;
|
|
}
|
|
|
|
.close-button:hover {
|
|
color: #333;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.form-input {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid #d9d9d9;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.form-input:focus {
|
|
outline: none;
|
|
border-color: #1890ff;
|
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
}
|
|
|
|
.form-input:read-only {
|
|
background-color: #f5f5f5;
|
|
color: #666;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.form-input:read-only:focus {
|
|
border-color: #d9d9d9;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
margin-top: 24px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.btn {
|
|
padding: 8px 16px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: #1890ff;
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
border: 1px solid #d9d9d9;
|
|
}
|
|
|
|
.btn:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.access-modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.access-modal-content {
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
padding: 24px;
|
|
width: 400px;
|
|
max-width: 90%;
|
|
}
|
|
|
|
.access-modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.access-modal-title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.access-close-button {
|
|
background: none;
|
|
border: none;
|
|
font-size: 20px;
|
|
cursor: pointer;
|
|
color: #999;
|
|
}
|
|
|
|
.access-close-button:hover {
|
|
color: #333;
|
|
}
|
|
|
|
.access-form-group {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.access-form-label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.access-form-select {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid #d9d9d9;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
box-sizing: border-box;
|
|
background-color: white;
|
|
}
|
|
|
|
.access-form-select:focus {
|
|
outline: none;
|
|
border-color: #1890ff;
|
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
|
}
|
|
|
|
.access-modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
margin-top: 24px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid #e0e0e0;
|
|
}
|
|
</style>
|
|
<div class="users-container">
|
|
<div class="users-header">
|
|
<div class="users-title">用户管理</div>
|
|
</div>
|
|
<div class="error-message"></div>
|
|
${this.users.length === 0 ?
|
|
'<div class="loading">正在加载用户数据...</div>' :
|
|
`<table class="users-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="col-id">ID</th>
|
|
<th class="col-username">用户名</th>
|
|
<th class="col-fullname">姓名</th>
|
|
<th class="col-access">权限等级</th>
|
|
<th class="col-actions">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${this.users.map(user => `
|
|
<tr>
|
|
<td class="col-id">${user.id}</td>
|
|
<td class="col-username">${user.username}</td>
|
|
<td class="col-fullname">${user.full_name || '-'}</td>
|
|
<td class="col-access">${this.getAccessLevelName(user.access_level)}</td>
|
|
<td class="col-actions">
|
|
<button class="action-button edit-info" onclick="this.getRootNode().host.handleEditInfo(${user.id})">更改信息</button>
|
|
<button class="action-button change-access" onclick="this.getRootNode().host.handleChangeAccessLevel(${user.id})">调整权限</button>
|
|
<button class="action-button reset-password" onclick="this.getRootNode().host.handleResetPassword(${user.id})">重置密码</button>
|
|
<button class="action-button delete" onclick="this.getRootNode().host.handleDelete(${user.id})">删除</button>
|
|
</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>`
|
|
}
|
|
</div>
|
|
|
|
<!-- 编辑用户信息模态对话框 -->
|
|
<div class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<div class="modal-title">编辑用户信息</div>
|
|
<button class="close-button" onclick="this.getRootNode().host.hideEditModal()">×</button>
|
|
</div>
|
|
<form class="edit-form">
|
|
<div class="form-group">
|
|
<label class="form-label">用户名</label>
|
|
<input type="text" class="form-input" name="username" required readonly>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">姓名</label>
|
|
<input type="text" class="form-input" name="full_name">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">邮箱</label>
|
|
<input type="email" class="form-input" name="email">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">电话</label>
|
|
<input type="text" class="form-input" name="phone">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">部门</label>
|
|
<input type="text" class="form-input" name="department">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">职位</label>
|
|
<input type="text" class="form-input" name="position">
|
|
</div>
|
|
</form>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="this.getRootNode().host.hideEditModal()">取消</button>
|
|
<button class="btn btn-primary" onclick="this.getRootNode().host.handleSaveUserInfo()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 调整权限级别模态对话框 -->
|
|
<div class="access-modal">
|
|
<div class="access-modal-content">
|
|
<div class="access-modal-header">
|
|
<div class="access-modal-title">调整权限级别</div>
|
|
<button class="access-close-button" onclick="this.getRootNode().host.hideAccessModal()">×</button>
|
|
</div>
|
|
<form class="access-form">
|
|
<div class="access-form-group">
|
|
<label class="access-form-label">权限级别</label>
|
|
<select class="access-form-select" name="access_level" required>
|
|
<option value="0">访客</option>
|
|
<option value="1">用户</option>
|
|
<option value="2">开发人员</option>
|
|
<option value="3">组长</option>
|
|
</select>
|
|
</div>
|
|
</form>
|
|
<div class="access-modal-footer">
|
|
<button class="btn btn-secondary" onclick="this.getRootNode().host.hideAccessModal()">取消</button>
|
|
<button class="btn btn-primary" onclick="this.getRootNode().host.handleSaveAccessLevel()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define('user-management', UserManagement);
|