Initial commit: Car Maintenance Management System

Author: Yang Lu

School: Liaoning Institute of Science and Technology

Major: Computer Science and Technology

Class: BZ246

Tech Stack:

- Backend: Spring Boot 2.7.18 + JPA + MySQL

- Frontend: HTML5 + CSS3 + JavaScript

Features:

- User Management (Admin/Staff/Customer roles)

- Vehicle Archive Management

- Service Order Management

- Parts Inventory Management

- Online Appointment Service

- Data Statistics and Analysis

Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
wangziqi
2026-01-07 14:28:50 +08:00
commit cfae122685
45 changed files with 5447 additions and 0 deletions

View File

@@ -0,0 +1,468 @@
// 管理员仪表板JavaScript
// 检查登录状态和权限
if (!utils.checkAuth() || !utils.hasRole('admin')) {
window.location.href = '../login.html';
}
// 页面加载时初始化
window.addEventListener('DOMContentLoaded', () => {
initializeDashboard();
loadOverviewData();
});
// 初始化仪表板
function initializeDashboard() {
const user = utils.getCurrentUser();
if (user) {
document.getElementById('userName').textContent = user.realName;
document.getElementById('userAvatar').textContent = user.realName.charAt(0);
}
}
// 切换显示区域
function showSection(sectionName) {
// 隐藏所有区域
const sections = document.querySelectorAll('.section');
sections.forEach(section => section.style.display = 'none');
// 移除所有菜单项的活动状态
const menuItems = document.querySelectorAll('.menu-item');
menuItems.forEach(item => item.classList.remove('active'));
// 显示选中的区域
document.getElementById(`${sectionName}-section`).style.display = 'block';
// 设置对应菜单项为活动状态
event.currentTarget.classList.add('active');
// 加载对应数据
switch(sectionName) {
case 'overview':
loadOverviewData();
break;
case 'users':
loadUsers();
break;
case 'vehicles':
loadVehicles();
break;
case 'orders':
loadOrders();
break;
case 'parts':
loadParts();
break;
case 'appointments':
loadAppointments();
break;
}
}
// 加载概览数据
async function loadOverviewData() {
try {
// 加载统计数据
const [usersRes, vehiclesRes, ordersRes, partsRes] = await Promise.all([
api.get(API_ENDPOINTS.USERS),
api.get(API_ENDPOINTS.VEHICLES),
api.get(API_ENDPOINTS.ORDERS),
api.get(API_ENDPOINTS.PARTS_LOW_STOCK)
]);
document.getElementById('totalUsers').textContent = usersRes.data?.length || 0;
document.getElementById('totalVehicles').textContent = vehiclesRes.data?.length || 0;
document.getElementById('totalOrders').textContent = ordersRes.data?.length || 0;
document.getElementById('lowStockParts').textContent = partsRes.data?.length || 0;
// 加载最近工单
if (ordersRes.data && ordersRes.data.length > 0) {
const recentOrders = ordersRes.data.slice(0, 5);
displayRecentOrders(recentOrders);
}
} catch (error) {
console.error('加载概览数据失败:', error);
}
}
// 显示最近工单
async function displayRecentOrders(orders) {
const tbody = document.getElementById('recentOrdersBody');
if (orders.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" class="empty-state">暂无数据</td></tr>';
return;
}
// 获取所有车辆信息
const vehiclesRes = await api.get(API_ENDPOINTS.VEHICLES);
const vehicles = vehiclesRes.data || [];
tbody.innerHTML = orders.map(order => {
const vehicle = vehicles.find(v => v.vehicleId === order.vehicleId);
return `
<tr>
<td>${order.orderNo}</td>
<td>${utils.getServiceTypeText(order.serviceType)}</td>
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
<td>${utils.getStatusBadge(order.status)}</td>
<td>${utils.formatDateTime(order.createTime)}</td>
</tr>
`;
}).join('');
}
// 加载用户列表
async function loadUsers() {
try {
const response = await api.get(API_ENDPOINTS.USERS);
if (response.code === 200 && response.data) {
displayUsers(response.data);
}
} catch (error) {
console.error('加载用户列表失败:', error);
utils.showError('加载用户列表失败');
}
}
// 显示用户列表
function displayUsers(users) {
const tbody = document.getElementById('usersTableBody');
if (users.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无数据</td></tr>';
return;
}
tbody.innerHTML = users.map(user => `
<tr>
<td>${user.userId}</td>
<td>${user.username}</td>
<td>${user.realName}</td>
<td>${user.phone}</td>
<td>${utils.getRoleText(user.role)}</td>
<td>${user.status === 1 ? '<span class="badge badge-success">启用</span>' : '<span class="badge badge-secondary">禁用</span>'}</td>
<td class="table-actions">
<button class="btn btn-info" onclick="viewUser(${user.userId})">查看</button>
<button class="btn btn-warning" onclick="editUser(${user.userId})">编辑</button>
<button class="btn btn-danger" onclick="deleteUser(${user.userId})">删除</button>
</td>
</tr>
`).join('');
}
// 加载车辆列表
async function loadVehicles() {
try {
const response = await api.get(API_ENDPOINTS.VEHICLES);
if (response.code === 200 && response.data) {
displayVehicles(response.data);
}
} catch (error) {
console.error('加载车辆列表失败:', error);
utils.showError('加载车辆列表失败');
}
}
// 显示车辆列表
function displayVehicles(vehicles) {
const tbody = document.getElementById('vehiclesTableBody');
if (vehicles.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="empty-state">暂无数据</td></tr>';
return;
}
tbody.innerHTML = vehicles.map(vehicle => `
<tr>
<td>${vehicle.licensePlate}</td>
<td>${vehicle.brand} ${vehicle.model}</td>
<td>${vehicle.color || '-'}</td>
<td>${vehicle.mileage || 0} 公里</td>
<td>${utils.getStatusBadge(vehicle.status)}</td>
<td class="table-actions">
<button class="btn btn-info" onclick="viewVehicle(${vehicle.vehicleId})">查看</button>
<button class="btn btn-warning" onclick="editVehicle(${vehicle.vehicleId})">编辑</button>
<button class="btn btn-danger" onclick="deleteVehicle(${vehicle.vehicleId})">删除</button>
</td>
</tr>
`).join('');
}
// 加载工单列表
async function loadOrders(status = '') {
try {
const url = status ? API_ENDPOINTS.ORDERS_BY_STATUS(status) : API_ENDPOINTS.ORDERS;
const response = await api.get(url);
if (response.code === 200 && response.data) {
displayOrders(response.data);
}
} catch (error) {
console.error('加载工单列表失败:', error);
utils.showError('加载工单列表失败');
}
}
// 显示工单列表
async function displayOrders(orders) {
const tbody = document.getElementById('ordersTableBody');
if (orders.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无数据</td></tr>';
return;
}
const vehiclesRes = await api.get(API_ENDPOINTS.VEHICLES);
const vehicles = vehiclesRes.data || [];
tbody.innerHTML = orders.map(order => {
const vehicle = vehicles.find(v => v.vehicleId === order.vehicleId);
return `
<tr>
<td>${order.orderNo}</td>
<td>${utils.getServiceTypeText(order.serviceType)}</td>
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
<td>¥${order.totalCost || 0}</td>
<td>${utils.getStatusBadge(order.status)}</td>
<td>${utils.formatDateTime(order.createTime)}</td>
<td class="table-actions">
<button class="btn btn-info" onclick="viewOrder(${order.orderId})">查看</button>
<button class="btn btn-warning" onclick="editOrder(${order.orderId})">编辑</button>
<button class="btn btn-danger" onclick="deleteOrder(${order.orderId})">删除</button>
</td>
</tr>
`;
}).join('');
}
// 加载配件列表
async function loadParts() {
try {
const response = await api.get(API_ENDPOINTS.PARTS);
if (response.code === 200 && response.data) {
displayParts(response.data);
}
} catch (error) {
console.error('加载配件列表失败:', error);
utils.showError('加载配件列表失败');
}
}
// 显示配件列表
function displayParts(parts) {
const tbody = document.getElementById('partsTableBody');
if (parts.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无数据</td></tr>';
return;
}
tbody.innerHTML = parts.map(part => {
const isLowStock = part.stockQuantity <= part.minStock;
return `
<tr ${isLowStock ? 'style="background-color: #fff1f0;"' : ''}>
<td>${part.partNo}</td>
<td>${part.partName}</td>
<td>${part.category || '-'}</td>
<td>${part.stockQuantity} ${part.unit}${isLowStock ? ' <span class="badge badge-danger">预警</span>' : ''}</td>
<td>¥${part.unitPrice}</td>
<td>${part.status === 1 ? '<span class="badge badge-success">正常</span>' : '<span class="badge badge-secondary">停用</span>'}</td>
<td class="table-actions">
<button class="btn btn-info" onclick="viewPart(${part.partId})">查看</button>
<button class="btn btn-warning" onclick="editPart(${part.partId})">编辑</button>
<button class="btn btn-danger" onclick="deletePart(${part.partId})">删除</button>
</td>
</tr>
`;
}).join('');
}
// 加载预约列表
async function loadAppointments(status = '') {
try {
const url = status ? API_ENDPOINTS.APPOINTMENTS_BY_STATUS(status) : API_ENDPOINTS.APPOINTMENTS;
const response = await api.get(url);
if (response.code === 200 && response.data) {
displayAppointments(response.data);
}
} catch (error) {
console.error('加载预约列表失败:', error);
utils.showError('加载预约列表失败');
}
}
// 显示预约列表
async function displayAppointments(appointments) {
const tbody = document.getElementById('appointmentsTableBody');
if (appointments.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" class="empty-state">暂无数据</td></tr>';
return;
}
const vehiclesRes = await api.get(API_ENDPOINTS.VEHICLES);
const vehicles = vehiclesRes.data || [];
tbody.innerHTML = appointments.map(appointment => {
const vehicle = vehicles.find(v => v.vehicleId === appointment.vehicleId);
return `
<tr>
<td>${appointment.appointmentId}</td>
<td>${utils.getServiceTypeText(appointment.serviceType)}</td>
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
<td>${utils.formatDateTime(appointment.appointmentTime)}</td>
<td>${appointment.contactPhone}</td>
<td>${utils.getStatusBadge(appointment.status)}</td>
<td class="table-actions">
<button class="btn btn-success" onclick="confirmAppointment(${appointment.appointmentId})">确认</button>
<button class="btn btn-danger" onclick="cancelAppointment(${appointment.appointmentId})">取消</button>
</td>
</tr>
`;
}).join('');
}
// 过滤工单
function filterOrders() {
const status = document.getElementById('orderStatusFilter').value;
loadOrders(status);
}
// 过滤预约
function filterAppointments() {
const status = document.getElementById('appointmentStatusFilter').value;
loadAppointments(status);
}
// 搜索用户
function searchUsers() {
const keyword = document.getElementById('searchUser').value.toLowerCase();
// 实现搜索逻辑
}
// 搜索车辆
function searchVehicles() {
const keyword = document.getElementById('searchVehicle').value.toLowerCase();
// 实现搜索逻辑
}
// 搜索配件
function searchParts() {
const keyword = document.getElementById('searchPart').value.toLowerCase();
// 实现搜索逻辑
}
// 显示低库存配件
async function showLowStockParts() {
try {
const response = await api.get(API_ENDPOINTS.PARTS_LOW_STOCK);
if (response.code === 200 && response.data) {
displayParts(response.data);
utils.showSuccess(`找到 ${response.data.length} 个库存预警配件`);
}
} catch (error) {
console.error('加载库存预警失败:', error);
utils.showError('加载库存预警失败');
}
}
// 占位函数 - 实际项目中需要实现完整功能
function showAddUserModal() { alert('添加用户功能'); }
function viewUser(id) { alert('查看用户: ' + id); }
function editUser(id) { alert('编辑用户: ' + id); }
async function deleteUser(id) {
if (utils.confirm('确定要删除此用户吗?')) {
try {
const response = await api.delete(API_ENDPOINTS.USER_BY_ID(id));
if (response.code === 200) {
utils.showSuccess('删除成功');
loadUsers();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('删除失败');
}
}
}
function showAddVehicleModal() { alert('添加车辆功能'); }
function viewVehicle(id) { alert('查看车辆: ' + id); }
function editVehicle(id) { alert('编辑车辆: ' + id); }
async function deleteVehicle(id) {
if (utils.confirm('确定要删除此车辆吗?')) {
try {
const response = await api.delete(API_ENDPOINTS.VEHICLE_BY_ID(id));
if (response.code === 200) {
utils.showSuccess('删除成功');
loadVehicles();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('删除失败');
}
}
}
function showAddOrderModal() { alert('创建工单功能'); }
function viewOrder(id) { alert('查看工单: ' + id); }
function editOrder(id) { alert('编辑工单: ' + id); }
async function deleteOrder(id) {
if (utils.confirm('确定要删除此工单吗?')) {
try {
const response = await api.delete(API_ENDPOINTS.ORDER_BY_ID(id));
if (response.code === 200) {
utils.showSuccess('删除成功');
loadOrders();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('删除失败');
}
}
}
function showAddPartModal() { alert('添加配件功能'); }
function viewPart(id) { alert('查看配件: ' + id); }
function editPart(id) { alert('编辑配件: ' + id); }
async function deletePart(id) {
if (utils.confirm('确定要删除此配件吗?')) {
try {
const response = await api.delete(API_ENDPOINTS.PART_BY_ID(id));
if (response.code === 200) {
utils.showSuccess('删除成功');
loadParts();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('删除失败');
}
}
}
async function confirmAppointment(id) {
try {
const response = await api.put(API_ENDPOINTS.APPOINTMENT_BY_ID(id), { status: 'confirmed' });
if (response.code === 200) {
utils.showSuccess('预约已确认');
loadAppointments();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('操作失败');
}
}
async function cancelAppointment(id) {
if (utils.confirm('确定要取消此预约吗?')) {
try {
const response = await api.put(API_ENDPOINTS.CANCEL_APPOINTMENT(id));
if (response.code === 200) {
utils.showSuccess('预约已取消');
loadAppointments();
} else {
utils.showError(response.message);
}
} catch (error) {
utils.showError('操作失败');
}
}
}

194
frontend/js/api.js Normal file
View File

@@ -0,0 +1,194 @@
// API请求工具类
class API {
constructor() {
this.baseURL = API_CONFIG.BASE_URL;
this.timeout = API_CONFIG.TIMEOUT;
}
// 获取请求头
getHeaders() {
const headers = {
'Content-Type': 'application/json'
};
const token = localStorage.getItem(STORAGE_KEYS.TOKEN);
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
// 通用请求方法
async request(url, options = {}) {
const config = {
method: options.method || 'GET',
headers: this.getHeaders(),
...options
};
if (options.body && typeof options.body === 'object') {
config.body = JSON.stringify(options.body);
}
try {
const response = await fetch(this.baseURL + url, config);
const data = await response.json();
if (data.code === 401) {
this.handleUnauthorized();
throw new Error('未授权,请重新登录');
}
return data;
} catch (error) {
console.error('API请求错误:', error);
throw error;
}
}
// GET请求
async get(url) {
return this.request(url, { method: 'GET' });
}
// POST请求
async post(url, body) {
return this.request(url, { method: 'POST', body });
}
// PUT请求
async put(url, body) {
return this.request(url, { method: 'PUT', body });
}
// DELETE请求
async delete(url) {
return this.request(url, { method: 'DELETE' });
}
// 处理未授权情况
handleUnauthorized() {
localStorage.removeItem(STORAGE_KEYS.TOKEN);
localStorage.removeItem(STORAGE_KEYS.USER_INFO);
window.location.href = 'login.html';
}
}
// 创建API实例
const api = new API();
// 工具函数
const utils = {
// 显示提示消息
showMessage(message, type = 'info') {
alert(message);
},
// 显示成功消息
showSuccess(message) {
this.showMessage(message, 'success');
},
// 显示错误消息
showError(message) {
this.showMessage(message, 'error');
},
// 确认对话框
confirm(message) {
return window.confirm(message);
},
// 格式化日期
formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN');
},
// 格式化日期时间
formatDateTime(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleString('zh-CN');
},
// 获取当前用户信息
getCurrentUser() {
const userStr = localStorage.getItem(STORAGE_KEYS.USER_INFO);
return userStr ? JSON.parse(userStr) : null;
},
// 检查用户角色
hasRole(role) {
const user = this.getCurrentUser();
return user && user.role === role;
},
// 退出登录
logout() {
if (this.confirm('确定要退出登录吗?')) {
localStorage.removeItem(STORAGE_KEYS.TOKEN);
localStorage.removeItem(STORAGE_KEYS.USER_INFO);
window.location.href = 'login.html';
}
},
// 检查登录状态
checkAuth() {
const token = localStorage.getItem(STORAGE_KEYS.TOKEN);
if (!token) {
window.location.href = 'login.html';
return false;
}
return true;
},
// 获取状态标签HTML
getStatusBadge(status, type) {
const badges = {
// 工单状态
pending: '<span class="badge badge-info">待处理</span>',
appointed: '<span class="badge badge-info">已预约</span>',
in_progress: '<span class="badge badge-warning">进行中</span>',
completed: '<span class="badge badge-success">已完成</span>',
cancelled: '<span class="badge badge-secondary">已取消</span>',
// 支付状态
unpaid: '<span class="badge badge-danger">未支付</span>',
paid: '<span class="badge badge-success">已支付</span>',
refunded: '<span class="badge badge-secondary">已退款</span>',
// 预约状态
confirmed: '<span class="badge badge-success">已确认</span>',
// 车辆状态
normal: '<span class="badge badge-success">正常</span>',
in_service: '<span class="badge badge-warning">维修中</span>'
};
return badges[status] || `<span class="badge badge-secondary">${status}</span>`;
},
// 获取服务类型文本
getServiceTypeText(type) {
const types = {
maintenance: '保养维护',
repair: '维修服务',
beauty: '美容服务',
insurance: '保险代理'
};
return types[type] || type;
},
// 获取用户角色文本
getRoleText(role) {
const roles = {
admin: '管理员',
staff: '工作人员',
customer: '客户'
};
return roles[role] || role;
}
};

52
frontend/js/config.js Normal file
View File

@@ -0,0 +1,52 @@
// API配置
const API_CONFIG = {
BASE_URL: 'http://localhost:8080/api',
TIMEOUT: 30000
};
// API端点
const API_ENDPOINTS = {
// 认证相关
LOGIN: '/auth/login',
LOGOUT: '/auth/logout',
REGISTER: '/auth/register',
// 用户管理
USERS: '/users',
USER_BY_ID: (id) => `/users/${id}`,
USERS_BY_ROLE: (role) => `/users/role/${role}`,
CHANGE_PASSWORD: (id) => `/users/${id}/password`,
// 车辆管理
VEHICLES: '/vehicles',
VEHICLE_BY_ID: (id) => `/vehicles/${id}`,
VEHICLES_BY_CUSTOMER: (customerId) => `/vehicles/customer/${customerId}`,
VEHICLE_BY_PLATE: (plate) => `/vehicles/plate/${plate}`,
// 工单管理
ORDERS: '/orders',
ORDER_BY_ID: (id) => `/orders/${id}`,
ORDERS_BY_CUSTOMER: (customerId) => `/orders/customer/${customerId}`,
ORDERS_BY_VEHICLE: (vehicleId) => `/orders/vehicle/${vehicleId}`,
ORDERS_BY_STATUS: (status) => `/orders/status/${status}`,
// 配件管理
PARTS: '/parts',
PART_BY_ID: (id) => `/parts/${id}`,
PARTS_BY_CATEGORY: (category) => `/parts/category/${category}`,
PARTS_LOW_STOCK: '/parts/low-stock',
// 预约管理
APPOINTMENTS: '/appointments',
APPOINTMENT_BY_ID: (id) => `/appointments/${id}`,
APPOINTMENTS_BY_CUSTOMER: (customerId) => `/appointments/customer/${customerId}`,
APPOINTMENTS_BY_STATUS: (status) => `/appointments/status/${status}`,
CANCEL_APPOINTMENT: (id) => `/appointments/${id}/cancel`
};
// 本地存储键名
const STORAGE_KEYS = {
TOKEN: 'car_maintenance_token',
USER_INFO: 'car_maintenance_user',
REMEMBER_ME: 'car_maintenance_remember'
};

102
frontend/js/login.js Normal file
View File

@@ -0,0 +1,102 @@
// 登录页面JavaScript
// 页面加载时检查是否已登录
window.addEventListener('DOMContentLoaded', () => {
const token = localStorage.getItem(STORAGE_KEYS.TOKEN);
if (token) {
const user = utils.getCurrentUser();
if (user) {
redirectToDashboard(user.role);
}
}
// 回车键登录
document.getElementById('password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
handleLogin();
}
});
});
// 处理登录
async function handleLogin() {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value.trim();
const role = document.getElementById('role').value;
// 验证输入
if (!username) {
utils.showError('请输入用户名');
return;
}
if (!password) {
utils.showError('请输入密码');
return;
}
// 禁用登录按钮
const loginBtn = document.querySelector('.btn-login');
const originalText = loginBtn.textContent;
loginBtn.disabled = true;
loginBtn.textContent = '登录中...';
try {
// 调用登录API
const response = await api.post(API_ENDPOINTS.LOGIN, {
username: username,
password: password
});
if (response.code === 200) {
const { token, userInfo } = response.data;
// 验证角色
if (userInfo.role !== role) {
utils.showError('登录角色不匹配,请选择正确的角色');
loginBtn.disabled = false;
loginBtn.textContent = originalText;
return;
}
// 保存登录信息
localStorage.setItem(STORAGE_KEYS.TOKEN, token);
localStorage.setItem(STORAGE_KEYS.USER_INFO, JSON.stringify(userInfo));
utils.showSuccess('登录成功!');
// 延迟跳转以显示成功消息
setTimeout(() => {
redirectToDashboard(userInfo.role);
}, 500);
} else {
utils.showError(response.message || '登录失败');
loginBtn.disabled = false;
loginBtn.textContent = originalText;
}
} catch (error) {
console.error('登录错误:', error);
utils.showError('登录失败,请检查网络连接');
loginBtn.disabled = false;
loginBtn.textContent = originalText;
}
}
// 根据角色跳转到对应的仪表板
function redirectToDashboard(role) {
switch (role) {
case 'admin':
window.location.href = 'admin/dashboard.html';
break;
case 'staff':
window.location.href = 'staff/dashboard.html';
break;
case 'customer':
window.location.href = 'customer/dashboard.html';
break;
default:
utils.showError('未知的用户角色');
}
}