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>
362 lines
16 KiB
HTML
362 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>客户中心 - 车管家4S店车辆维保管理系统</title>
|
||
<link rel="stylesheet" href="../css/common.css">
|
||
<link rel="stylesheet" href="../css/dashboard.css">
|
||
</head>
|
||
<body>
|
||
<div class="dashboard-container">
|
||
<div class="sidebar">
|
||
<div class="sidebar-header">
|
||
<h2>车管家系统</h2>
|
||
<p>客户中心</p>
|
||
</div>
|
||
<div class="sidebar-menu">
|
||
<div class="menu-item active" onclick="showSection('myvehicles')">
|
||
<span class="menu-icon">🚗</span>
|
||
<span>我的车辆</span>
|
||
</div>
|
||
<div class="menu-item" onclick="showSection('myorders')">
|
||
<span class="menu-icon">📋</span>
|
||
<span>维保记录</span>
|
||
</div>
|
||
<div class="menu-item" onclick="showSection('appointments')">
|
||
<span class="menu-icon">📅</span>
|
||
<span>我的预约</span>
|
||
</div>
|
||
<div class="menu-item" onclick="showSection('newappointment')">
|
||
<span class="menu-icon">➕</span>
|
||
<span>在线预约</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="main-content">
|
||
<div class="top-nav">
|
||
<div class="top-nav-left">
|
||
<h1>客户中心</h1>
|
||
</div>
|
||
<div class="top-nav-right">
|
||
<div class="user-info">
|
||
<div class="user-avatar" id="userAvatar">C</div>
|
||
<span class="user-name" id="userName">客户</span>
|
||
</div>
|
||
<button class="btn-logout" onclick="utils.logout()">退出登录</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="myvehicles-section" class="section">
|
||
<div class="content-card">
|
||
<div class="content-header">
|
||
<h2>我的车辆</h2>
|
||
</div>
|
||
<div class="content-body">
|
||
<div id="vehiclesGrid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;">
|
||
<p class="empty-state">加载中...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="myorders-section" class="section" style="display: none;">
|
||
<div class="content-card">
|
||
<div class="content-header">
|
||
<h2>维保记录</h2>
|
||
</div>
|
||
<div class="content-body">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>工单编号</th>
|
||
<th>服务类型</th>
|
||
<th>车牌号</th>
|
||
<th>费用</th>
|
||
<th>状态</th>
|
||
<th>创建时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="ordersBody">
|
||
<tr><td colspan="6" class="empty-state">加载中...</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="appointments-section" class="section" style="display: none;">
|
||
<div class="content-card">
|
||
<div class="content-header">
|
||
<h2>我的预约</h2>
|
||
</div>
|
||
<div class="content-body">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>服务类型</th>
|
||
<th>车牌号</th>
|
||
<th>预约时间</th>
|
||
<th>状态</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="appointmentsBody">
|
||
<tr><td colspan="5" class="empty-state">加载中...</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="newappointment-section" class="section" style="display: none;">
|
||
<div class="content-card">
|
||
<div class="content-header">
|
||
<h2>在线预约服务</h2>
|
||
</div>
|
||
<div class="content-body">
|
||
<form id="appointmentForm" style="max-width:600px;">
|
||
<div class="form-group">
|
||
<label>选择车辆</label>
|
||
<select id="vehicleSelect" required>
|
||
<option value="">请选择车辆</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>服务类型</label>
|
||
<select id="serviceType" required>
|
||
<option value="maintenance">保养维护</option>
|
||
<option value="repair">维修服务</option>
|
||
<option value="beauty">美容服务</option>
|
||
<option value="insurance">保险代理</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>预约时间</label>
|
||
<input type="datetime-local" id="appointmentTime" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>联系电话</label>
|
||
<input type="tel" id="contactPhone" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>预约说明</label>
|
||
<textarea id="description" placeholder="请描述您的需求"></textarea>
|
||
</div>
|
||
<button type="submit" class="btn btn-primary">提交预约</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="../js/config.js"></script>
|
||
<script src="../js/api.js"></script>
|
||
<script>
|
||
if (!utils.checkAuth() || !utils.hasRole('customer')) {
|
||
window.location.href = '../login.html';
|
||
}
|
||
|
||
let currentCustomerId = null;
|
||
|
||
window.addEventListener('DOMContentLoaded', async () => {
|
||
const user = utils.getCurrentUser();
|
||
if (user) {
|
||
document.getElementById('userName').textContent = user.realName;
|
||
document.getElementById('userAvatar').textContent = user.realName.charAt(0);
|
||
document.getElementById('contactPhone').value = user.phone;
|
||
|
||
await loadCustomerData(user.userId);
|
||
}
|
||
});
|
||
|
||
async function loadCustomerData(userId) {
|
||
try {
|
||
const usersRes = await api.get(API_ENDPOINTS.USERS);
|
||
const customers = usersRes.data?.filter(u => u.role === 'customer') || [];
|
||
const currentUser = customers.find(c => c.userId === userId);
|
||
|
||
if (currentUser) {
|
||
currentCustomerId = currentUser.userId;
|
||
loadVehicles();
|
||
loadOrders();
|
||
loadAppointments();
|
||
loadVehicleOptions();
|
||
}
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error);
|
||
}
|
||
}
|
||
|
||
async function loadVehicles() {
|
||
try {
|
||
const response = await api.get(API_ENDPOINTS.VEHICLES);
|
||
if (response.code === 200 && response.data) {
|
||
const myVehicles = response.data.filter(v => v.customerId === currentCustomerId);
|
||
displayVehicles(myVehicles);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载车辆失败:', error);
|
||
}
|
||
}
|
||
|
||
function displayVehicles(vehicles) {
|
||
const grid = document.getElementById('vehiclesGrid');
|
||
if (vehicles.length === 0) {
|
||
grid.innerHTML = '<p class="empty-state">暂无车辆</p>';
|
||
return;
|
||
}
|
||
grid.innerHTML = vehicles.map(v => `
|
||
<div class="card">
|
||
<h3 style="color:#1890ff;">${v.licensePlate}</h3>
|
||
<p><strong>品牌:</strong> ${v.brand} ${v.model}</p>
|
||
<p><strong>颜色:</strong> ${v.color || '-'}</p>
|
||
<p><strong>里程:</strong> ${v.mileage || 0} 公里</p>
|
||
<p><strong>上次保养:</strong> ${utils.formatDate(v.lastMaintenanceDate)}</p>
|
||
<p><strong>下次保养:</strong> ${utils.formatDate(v.nextMaintenanceDate)}</p>
|
||
<p><strong>状态:</strong> ${utils.getStatusBadge(v.status)}</p>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
async function loadOrders() {
|
||
try {
|
||
const [ordersRes, vehiclesRes] = await Promise.all([
|
||
api.get(API_ENDPOINTS.ORDERS),
|
||
api.get(API_ENDPOINTS.VEHICLES)
|
||
]);
|
||
|
||
if (ordersRes.code === 200 && ordersRes.data) {
|
||
const myOrders = ordersRes.data.filter(o => o.customerId === currentCustomerId);
|
||
const vehicles = vehiclesRes.data || [];
|
||
const tbody = document.getElementById('ordersBody');
|
||
|
||
if (myOrders.length === 0) {
|
||
tbody.innerHTML = '<tr><td colspan="6" class="empty-state">暂无维保记录</td></tr>';
|
||
return;
|
||
}
|
||
|
||
tbody.innerHTML = myOrders.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>¥${o.totalCost || 0}</td>
|
||
<td>${utils.getStatusBadge(o.status)}</td>
|
||
<td>${utils.formatDateTime(o.createTime)}</td>
|
||
</tr>
|
||
`;
|
||
}).join('');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载工单失败:', error);
|
||
}
|
||
}
|
||
|
||
async function loadAppointments() {
|
||
try {
|
||
const [appointmentsRes, vehiclesRes] = await Promise.all([
|
||
api.get(API_ENDPOINTS.APPOINTMENTS),
|
||
api.get(API_ENDPOINTS.VEHICLES)
|
||
]);
|
||
|
||
if (appointmentsRes.code === 200 && appointmentsRes.data) {
|
||
const myAppointments = appointmentsRes.data.filter(a => a.customerId === currentCustomerId);
|
||
const vehicles = vehiclesRes.data || [];
|
||
const tbody = document.getElementById('appointmentsBody');
|
||
|
||
if (myAppointments.length === 0) {
|
||
tbody.innerHTML = '<tr><td colspan="5" class="empty-state">暂无预约记录</td></tr>';
|
||
return;
|
||
}
|
||
|
||
tbody.innerHTML = myAppointments.map(a => {
|
||
const vehicle = vehicles.find(v => v.vehicleId === a.vehicleId);
|
||
return `
|
||
<tr>
|
||
<td>${utils.getServiceTypeText(a.serviceType)}</td>
|
||
<td>${vehicle ? vehicle.licensePlate : '-'}</td>
|
||
<td>${utils.formatDateTime(a.appointmentTime)}</td>
|
||
<td>${utils.getStatusBadge(a.status)}</td>
|
||
<td>
|
||
${a.status === 'pending' ? `<button class="btn btn-danger" onclick="cancelAppointment(${a.appointmentId})">取消</button>` : '-'}
|
||
</td>
|
||
</tr>
|
||
`;
|
||
}).join('');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载预约失败:', error);
|
||
}
|
||
}
|
||
|
||
async function loadVehicleOptions() {
|
||
try {
|
||
const response = await api.get(API_ENDPOINTS.VEHICLES);
|
||
if (response.code === 200 && response.data) {
|
||
const myVehicles = response.data.filter(v => v.customerId === currentCustomerId);
|
||
const select = document.getElementById('vehicleSelect');
|
||
select.innerHTML = '<option value="">请选择车辆</option>' +
|
||
myVehicles.map(v => `<option value="${v.vehicleId}">${v.licensePlate} - ${v.brand} ${v.model}</option>`).join('');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载车辆选项失败:', error);
|
||
}
|
||
}
|
||
|
||
document.getElementById('appointmentForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
|
||
const data = {
|
||
customerId: currentCustomerId,
|
||
vehicleId: parseInt(document.getElementById('vehicleSelect').value),
|
||
serviceType: document.getElementById('serviceType').value,
|
||
appointmentTime: document.getElementById('appointmentTime').value,
|
||
contactPhone: document.getElementById('contactPhone').value,
|
||
description: document.getElementById('description').value
|
||
};
|
||
|
||
try {
|
||
const response = await api.post(API_ENDPOINTS.APPOINTMENTS, data);
|
||
if (response.code === 200) {
|
||
utils.showSuccess('预约成功!');
|
||
document.getElementById('appointmentForm').reset();
|
||
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('操作失败');
|
||
}
|
||
}
|
||
}
|
||
|
||
function showSection(name) {
|
||
document.querySelectorAll('.section').forEach(s => s.style.display = 'none');
|
||
document.querySelectorAll('.menu-item').forEach(m => m.classList.remove('active'));
|
||
event.currentTarget.classList.add('active');
|
||
document.getElementById(name + '-section').style.display = 'block';
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|