This commit is contained in:
wangziqi
2026-01-07 15:39:54 +08:00
parent 81e6c3eeb8
commit 7c4be0b7b4
20 changed files with 3792 additions and 832 deletions

699
frontend/js/admin.js Normal file
View File

@@ -0,0 +1,699 @@
// 管理员仪表板专用JavaScript
// 全局数据存储
let allUsersData = [];
let allVehiclesData = [];
let allOrdersData = [];
let allPartsData = [];
// ==================== 用户管理 ====================
// 查看用户详情
async function viewUser(id) {
Utils.loading(true);
try {
const response = await http.get(API.USER(id));
if (response.code === 200 && response.data) {
const user = response.data;
// 填充表单(只读模式)
document.getElementById('userId').value = user.userId;
document.getElementById('userUsername').value = user.username;
document.getElementById('userRealName').value = user.realName;
document.getElementById('userPhone').value = user.phone;
document.getElementById('userEmail').value = user.email || '';
document.getElementById('userRole').value = user.role;
document.getElementById('userStatus').value = user.status;
// 禁用表单控件
document.getElementById('userForm').querySelectorAll('input, select').forEach(el => {
el.disabled = true;
});
document.getElementById('userModalTitle').textContent = '查看用户详情';
const modal = new bootstrap.Modal(document.getElementById('userModal'));
modal.show();
// 启用表单控件(在关闭时)
document.getElementById('userModal').addEventListener('hidden.bs.modal', function() {
document.getElementById('userForm').querySelectorAll('input, select').forEach(el => {
el.disabled = false;
});
}, { once: true });
}
} catch (error) {
Utils.showToast('加载用户信息失败', 'error');
} finally {
Utils.loading(false);
}
}
// 编辑用户
async function editUser(id) {
const user = allUsersData.find(u => u.userId === id);
if (!user) {
Utils.showToast('用户不存在', 'error');
return;
}
// 填充表单
document.getElementById('userId').value = user.userId;
document.getElementById('userUsername').value = user.username;
document.getElementById('userRealName').value = user.realName;
document.getElementById('userPhone').value = user.phone;
document.getElementById('userEmail').value = user.email || '';
document.getElementById('userRole').value = user.role;
document.getElementById('userStatus').value = user.status;
document.getElementById('userModalTitle').textContent = '编辑用户';
const modal = new bootstrap.Modal(document.getElementById('userModal'));
modal.show();
}
// 保存用户
async function saveUser() {
const userId = document.getElementById('userId').value;
const userData = {
username: document.getElementById('userUsername').value,
realName: document.getElementById('userRealName').value,
phone: document.getElementById('userPhone').value,
email: document.getElementById('userEmail').value,
role: document.getElementById('userRole').value,
status: parseInt(document.getElementById('userStatus').value)
};
Utils.loading(true);
try {
let response;
if (userId) {
// 更新
response = await http.put(API.USER(userId), userData);
} else {
// 新增
userData.password = '123456'; // 默认密码
response = await http.post(API.USERS, userData);
}
if (response.code === 200) {
Utils.showToast(userId ? '更新成功' : '创建成功', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('userModal'));
modal.hide();
loadAllUsers();
} else {
Utils.showToast(response.message || '保存失败', 'error');
}
} catch (error) {
console.error('保存用户失败:', error);
Utils.showToast('保存失败', 'error');
} finally {
Utils.loading(false);
}
}
// 添加用户
function showAddUserModal() {
// 清空表单
document.getElementById('userForm').reset();
document.getElementById('userId').value = '';
document.getElementById('userStatus').value = '1';
document.getElementById('userModalTitle').textContent = '添加用户';
const modal = new bootstrap.Modal(document.getElementById('userModal'));
modal.show();
}
// ==================== 车辆管理 ====================
// 查看车辆详情
async function viewVehicle(id) {
Utils.loading(true);
try {
const response = await http.get(API.VEHICLE(id));
if (response.code === 200 && response.data) {
const vehicle = response.data;
document.getElementById('vehicleId').value = vehicle.vehicleId;
document.getElementById('vehicleLicensePlate').value = vehicle.licensePlate;
document.getElementById('vehicleBrand').value = vehicle.brand;
document.getElementById('vehicleModel').value = vehicle.model;
document.getElementById('vehicleColor').value = vehicle.color || '';
document.getElementById('vehicleMileage').value = vehicle.mileage || 0;
document.getElementById('vehicleStatus').value = vehicle.status;
// 禁用表单
document.getElementById('vehicleForm').querySelectorAll('input, select').forEach(el => {
el.disabled = true;
});
document.getElementById('vehicleModalTitle').textContent = '查看车辆详情';
const modal = new bootstrap.Modal(document.getElementById('vehicleModal'));
modal.show();
document.getElementById('vehicleModal').addEventListener('hidden.bs.modal', function() {
document.getElementById('vehicleForm').querySelectorAll('input, select').forEach(el => {
el.disabled = false;
});
}, { once: true });
}
} catch (error) {
Utils.showToast('加载车辆信息失败', 'error');
} finally {
Utils.loading(false);
}
}
// 编辑车辆
async function editVehicle(id) {
const vehicle = allVehiclesData.find(v => v.vehicleId === id);
if (!vehicle) {
Utils.showToast('车辆不存在', 'error');
return;
}
document.getElementById('vehicleId').value = vehicle.vehicleId;
document.getElementById('vehicleLicensePlate').value = vehicle.licensePlate;
document.getElementById('vehicleBrand').value = vehicle.brand;
document.getElementById('vehicleModel').value = vehicle.model;
document.getElementById('vehicleColor').value = vehicle.color || '';
document.getElementById('vehicleMileage').value = vehicle.mileage || 0;
document.getElementById('vehicleStatus').value = vehicle.status;
document.getElementById('vehicleModalTitle').textContent = '编辑车辆';
const modal = new bootstrap.Modal(document.getElementById('vehicleModal'));
modal.show();
}
// 保存车辆
async function saveVehicle() {
const vehicleId = document.getElementById('vehicleId').value;
const vehicleData = {
licensePlate: document.getElementById('vehicleLicensePlate').value,
brand: document.getElementById('vehicleBrand').value,
model: document.getElementById('vehicleModel').value,
color: document.getElementById('vehicleColor').value,
mileage: parseFloat(document.getElementById('vehicleMileage').value) || 0,
status: document.getElementById('vehicleStatus').value
};
// 获取客户ID这里简化处理实际应该让用户选择
if (!vehicleId) {
vehicleData.customerId = 1; // 默认使用第一个客户
}
Utils.loading(true);
try {
let response;
if (vehicleId) {
response = await http.put(API.VEHICLE(vehicleId), vehicleData);
} else {
response = await http.post(API.VEHICLES, vehicleData);
}
if (response.code === 200) {
Utils.showToast(vehicleId ? '更新成功' : '添加成功', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('vehicleModal'));
modal.hide();
loadAllVehicles();
} else {
Utils.showToast(response.message || '保存失败', 'error');
}
} catch (error) {
console.error('保存车辆失败:', error);
Utils.showToast('保存失败', 'error');
} finally {
Utils.loading(false);
}
}
// 添加车辆
function showAddVehicleModal() {
document.getElementById('vehicleForm').reset();
document.getElementById('vehicleId').value = '';
document.getElementById('vehicleMileage').value = 0;
document.getElementById('vehicleModalTitle').textContent = '添加车辆';
const modal = new bootstrap.Modal(document.getElementById('vehicleModal'));
modal.show();
}
// ==================== 工单管理 ====================
// 查看工单详情
async function viewOrder(id) {
Utils.loading(true);
try {
const response = await http.get(API.ORDER(id));
if (response.code === 200 && response.data) {
const order = response.data;
document.getElementById('orderId').value = order.orderId;
document.getElementById('orderServiceType').value = order.serviceType;
document.getElementById('orderFaultDescription').value = order.faultDescription || '';
document.getElementById('orderDiagnosisResult').value = order.diagnosisResult || '';
document.getElementById('orderPartsCost').value = order.partsCost || 0;
document.getElementById('orderLaborCost').value = order.laborCost || 0;
document.getElementById('orderTotalCost').value = order.totalCost || 0;
document.getElementById('orderStatus').value = order.status;
// 禁用表单
document.getElementById('orderForm').querySelectorAll('input, select, textarea').forEach(el => {
el.disabled = true;
});
document.getElementById('orderModalTitle').textContent = '查看工单详情';
const modal = new bootstrap.Modal(document.getElementById('orderModal'));
modal.show();
document.getElementById('orderModal').addEventListener('hidden.bs.modal', function() {
document.getElementById('orderForm').querySelectorAll('input, select, textarea').forEach(el => {
el.disabled = false;
});
}, { once: true });
}
} catch (error) {
Utils.showToast('加载工单信息失败', 'error');
} finally {
Utils.loading(false);
}
}
// 编辑工单
async function editOrder(id) {
const order = allOrdersData.find(o => o.orderId === id);
if (!order) {
Utils.showToast('工单不存在', 'error');
return;
}
document.getElementById('orderId').value = order.orderId;
document.getElementById('orderServiceType').value = order.serviceType;
document.getElementById('orderFaultDescription').value = order.faultDescription || '';
document.getElementById('orderDiagnosisResult').value = order.diagnosisResult || '';
document.getElementById('orderPartsCost').value = order.partsCost || 0;
document.getElementById('orderLaborCost').value = order.laborCost || 0;
document.getElementById('orderTotalCost').value = order.totalCost || 0;
document.getElementById('orderStatus').value = order.status;
document.getElementById('orderModalTitle').textContent = '编辑工单';
const modal = new bootstrap.Modal(document.getElementById('orderModal'));
modal.show();
// 监听费用变化自动计算总额
setupOrderCostCalculation();
}
// 设置工单费用自动计算
function setupOrderCostCalculation() {
const partsCost = document.getElementById('orderPartsCost');
const laborCost = document.getElementById('orderLaborCost');
const totalCost = document.getElementById('orderTotalCost');
function updateTotal() {
const parts = parseFloat(partsCost.value) || 0;
const labor = parseFloat(laborCost.value) || 0;
totalCost.value = (parts + labor).toFixed(2);
}
partsCost.addEventListener('input', updateTotal);
laborCost.addEventListener('input', updateTotal);
}
// 保存工单
async function saveOrder() {
const orderId = document.getElementById('orderId').value;
const orderData = {
serviceType: document.getElementById('orderServiceType').value,
faultDescription: document.getElementById('orderFaultDescription').value,
diagnosisResult: document.getElementById('orderDiagnosisResult').value,
partsCost: parseFloat(document.getElementById('orderPartsCost').value) || 0,
laborCost: parseFloat(document.getElementById('orderLaborCost').value) || 0,
totalCost: parseFloat(document.getElementById('orderTotalCost').value) || 0,
status: document.getElementById('orderStatus').value
};
Utils.loading(true);
try {
// 更新工单
const response = await http.put(API.ORDER(orderId), orderData);
if (response.code === 200) {
Utils.showToast('更新成功', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('orderModal'));
modal.hide();
loadAllOrders();
} else {
Utils.showToast(response.message || '保存失败', 'error');
}
} catch (error) {
console.error('保存工单失败:', error);
Utils.showToast('保存失败', 'error');
} finally {
Utils.loading(false);
}
}
// 添加工单
function showAddOrderModal() {
document.getElementById('orderForm').reset();
document.getElementById('orderId').value = '';
document.getElementById('orderPartsCost').value = 0;
document.getElementById('orderLaborCost').value = 0;
document.getElementById('orderTotalCost').value = 0;
document.getElementById('orderModalTitle').textContent = '创建工单';
const modal = new bootstrap.Modal(document.getElementById('orderModal'));
modal.show();
setupOrderCostCalculation();
}
// 过滤工单
function filterOrders(status) {
if (!status) {
displayAllOrders(allOrdersData);
} else {
const filtered = allOrdersData.filter(o => o.status === status);
displayAllOrders(filtered);
}
}
// ==================== 配件管理 ====================
// 查看配件详情
async function viewPart(id) {
Utils.loading(true);
try {
const response = await http.get(API.PART(id));
if (response.code === 200 && response.data) {
const part = response.data;
// 显示配件详情
Utils.showToast('配件名称: ' + part.partName + '\n库存: ' + part.stockQuantity + part.unit, 'info');
}
} catch (error) {
Utils.showToast('加载配件信息失败', 'error');
} finally {
Utils.loading(false);
}
}
// 编辑配件
async function editPart(id) {
const part = allPartsData.find(p => p.partId === id);
if (!part) {
Utils.showToast('配件不存在', 'error');
return;
}
// 简单实现使用prompt编辑
const newStock = prompt('请输入新的库存数量:', part.stockQuantity);
if (newStock !== null && !isNaN(newStock)) {
Utils.loading(true);
try {
const response = await http.put(API.PART(id), {
...part,
stockQuantity: parseInt(newStock)
});
if (response.code === 200) {
Utils.showToast('更新成功', 'success');
loadAllParts();
} else {
Utils.showToast(response.message || '更新失败', 'error');
}
} catch (error) {
Utils.showToast('更新失败', 'error');
} finally {
Utils.loading(false);
}
}
}
// 添加配件
function showAddPartModal() {
Utils.showToast('添加配件功能开发中...', 'info');
}
// ==================== 数据加载函数 ====================
// 加载所有数据
async function loadAllData() {
await Promise.all([
loadAllUsers(),
loadAllVehicles(),
loadAllOrders(),
loadAllParts()
]);
}
// 加载所有配件
async function loadAllParts() {
Utils.loading(true);
try {
const response = await http.get(API.PARTS);
if (response.code === 200) {
allPartsData = response.data || [];
displayAllParts(allPartsData);
}
} catch (error) {
console.error('加载配件失败:', error);
} finally {
Utils.loading(false);
}
}
// 显示所有配件
function displayAllParts(parts) {
const tbody = document.getElementById('partsTableBody');
if (!tbody) return;
if (parts.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4">暂无数据</td></tr>';
return;
}
tbody.innerHTML = parts.map(p => `
<tr>
<td>${p.partNo}</td>
<td>${p.partName}</td>
<td>${p.category || '-'}</td>
<td>
${p.stockQuantity} ${p.unit}
${p.stockQuantity <= p.minStock ? '<span class="badge bg-danger ms-2">库存预警</span>' : ''}
</td>
<td>${Utils.formatMoney(p.unitPrice)}</td>
<td>
<button class="btn btn-sm btn-info" onclick="viewPart(${p.partId})">查看</button>
<button class="btn btn-sm btn-warning" onclick="editPart(${p.partId})">编辑</button>
<button class="btn btn-sm btn-danger" onclick="deletePart(${p.partId})">删除</button>
</td>
</tr>
`).join('');
}
// 删除配件
async function deletePart(id) {
if (!Utils.confirm('确定要删除此配件吗?')) return;
Utils.loading(true);
try {
const response = await http.delete(API.PART(id));
if (response.code === 200) {
Utils.showToast('删除成功', 'success');
loadAllParts();
} else {
Utils.showToast(response.message || '删除失败', 'error');
}
} catch (error) {
Utils.showToast('删除失败', 'error');
} finally {
Utils.loading(false);
}
}
// ==================== 页面初始化 ====================
// 重写displayAllUsers以存储数据
const originalDisplayAllUsers = displayAllUsers;
displayAllUsers = function(users) {
allUsersData = users || [];
const tbody = document.getElementById('allUsersTableBody');
if (!tbody) return;
if (users.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="text-center text-muted py-4">暂无数据</td></tr>';
return;
}
tbody.innerHTML = users.map(u => `
<tr>
<td>${u.userId}</td>
<td>${u.username}</td>
<td>${u.realName}</td>
<td>${u.phone}</td>
<td>${Utils.getRoleText(u.role)}</td>
<td>${u.status === 1 ? '<span class="badge bg-success">启用</span>' : '<span class="badge bg-secondary">禁用</span>'}</td>
<td>
<button class="btn btn-sm btn-info" onclick="viewUser(${u.userId})">查看</button>
<button class="btn btn-sm btn-warning" onclick="editUser(${u.userId})">编辑</button>
<button class="btn btn-sm btn-danger" onclick="deleteUser(${u.userId})">删除</button>
</td>
</tr>
`).join('');
};
// 重写displayAllVehicles以存储数据
const originalDisplayAllVehicles = displayAllVehicles;
displayAllVehicles = function(vehicles) {
allVehiclesData = vehicles || [];
const tbody = document.getElementById('allVehiclesTableBody');
if (!tbody) return;
if (vehicles.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4">暂无数据</td></tr>';
return;
}
tbody.innerHTML = vehicles.map(v => `
<tr>
<td>${v.licensePlate}</td>
<td>${v.brand} ${v.model}</td>
<td>${v.color || '-'}</td>
<td>${v.mileage || 0}</td>
<td>${Utils.getStatusBadge(v.status)}</td>
<td>
<button class="btn btn-sm btn-info" onclick="viewVehicle(${v.vehicleId})">查看</button>
<button class="btn btn-sm btn-warning" onclick="editVehicle(${v.vehicleId})">编辑</button>
<button class="btn btn-sm btn-danger" onclick="deleteVehicle(${v.vehicleId})">删除</button>
</td>
</tr>
`).join('');
};
// 重写displayAllOrders以存储数据
const originalDisplayAllOrders = displayAllOrders;
displayAllOrders = async function(orders) {
allOrdersData = orders || [];
const tbody = document.getElementById('allOrdersTableBody');
if (!tbody) return;
if (orders.length === 0) {
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-muted py-4">暂无数据</td></tr>';
return;
}
// 获取所有车辆信息
const vehiclesRes = await http.get(API.VEHICLES);
const vehicles = vehiclesRes.data || [];
tbody.innerHTML = orders.map(o => {
const vehicle = vehicles.find(v => v.vehicleId === o.vehicleId);
return `
<tr>
<td>${o.orderNo}</td>
<td>${Utils.getServiceTypeText(o.serviceType)}</td>
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
<td>${Utils.formatMoney(o.totalCost)}</td>
<td>${Utils.getStatusBadge(o.status)}</td>
<td>${Utils.getStatusBadge(o.paymentStatus, 'payment')}</td>
<td>${Utils.formatDateTime(o.createTime)}</td>
<td>
<button class="btn btn-sm btn-info" onclick="viewOrder(${o.orderId})">查看</button>
<button class="btn btn-sm btn-warning" onclick="editOrder(${o.orderId})">编辑</button>
<button class="btn btn-sm btn-danger" onclick="deleteOrder(${o.orderId})">删除</button>
</td>
</tr>
`;
}).join('');
};
// 加载预约数据
async function loadAppointments() {
Utils.loading(true);
try {
const [appointmentsRes, vehiclesRes] = await Promise.all([
http.get(API.APPOINTMENTS),
http.get(API.VEHICLES)
]);
if (appointmentsRes.code === 200) {
const vehicles = vehiclesRes.data || [];
displayAppointments(appointmentsRes.data || [], vehicles);
}
} catch (error) {
console.error('加载预约失败:', error);
} finally {
Utils.loading(false);
}
}
// 显示预约列表
function displayAppointments(appointments, vehicles) {
const tbody = document.getElementById('appointmentsTableBody');
if (!tbody) return;
if (appointments.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="text-center text-muted py-4">暂无数据</td></tr>';
return;
}
tbody.innerHTML = appointments.map(a => {
const vehicle = vehicles.find(v => v.vehicleId === a.vehicleId);
return `
<tr>
<td>${a.appointmentId}</td>
<td>${Utils.getServiceTypeText(a.serviceType)}</td>
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
<td>${Utils.formatDateTime(a.appointmentTime)}</td>
<td>${a.contactPhone}</td>
<td>${Utils.getStatusBadge(a.status, 'appointment')}</td>
<td>
<button class="btn btn-sm btn-success" onclick="confirmAppointment(${a.appointmentId})">确认</button>
<button class="btn btn-sm btn-danger" onclick="cancelAppointmentAdmin(${a.appointmentId})">取消</button>
</td>
</tr>
`;
}).join('');
}
// 管理员确认预约
async function confirmAppointment(id) {
Utils.loading(true);
try {
const response = await http.put(API.APPOINTMENT(id), { status: 'confirmed' });
if (response.code === 200) {
Utils.showToast('预约已确认', 'success');
loadAppointments();
} else {
Utils.showToast(response.message || '操作失败', 'error');
}
} catch (error) {
Utils.showToast('操作失败', 'error');
} finally {
Utils.loading(false);
}
}
// 管理员取消预约
async function cancelAppointmentAdmin(id) {
if (!Utils.confirm('确定要取消此预约吗?')) return;
Utils.loading(true);
try {
const response = await http.put(API.APPOINTMENT_CANCEL(id), {});
if (response.code === 200) {
Utils.showToast('预约已取消', 'success');
loadAppointments();
} else {
Utils.showToast(response.message || '操作失败', 'error');
}
} catch (error) {
Utils.showToast('操作失败', 'error');
} finally {
Utils.loading(false);
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 如果是管理员页面,加载所有数据
if (window.location.pathname.includes('admin')) {
loadAllData();
}
});