410 lines
16 KiB
HTML
410 lines
16 KiB
HTML
{% extends "user/base.html" %}
|
|
|
|
{% block title %}售后服务 - {{ config.SITE_NAME or '软件授权管理系统' }}{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.ticket-form {
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
padding: 2rem;
|
|
}
|
|
|
|
.ticket-history {
|
|
border-left: 3px solid #667eea;
|
|
}
|
|
|
|
.ticket-item {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.ticket-status-open {
|
|
background-color: #d1ecf1;
|
|
border-color: #bee5eb;
|
|
}
|
|
|
|
.ticket-status-closed {
|
|
background-color: #d4edda;
|
|
border-color: #c3e6cb;
|
|
}
|
|
|
|
.emergency-contact {
|
|
background: linear-gradient(135deg, #ff6b6b, #ee5a24);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-4">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h1 class="mb-4">售后服务</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- 紧急通道 -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="emergency-contact">
|
|
<h3 class="mb-3">
|
|
<i class="fas fa-exclamation-circle me-2"></i>
|
|
紧急通道
|
|
</h3>
|
|
<p class="mb-4">遇到紧急问题?立即联系我们</p>
|
|
<button class="btn btn-light" data-bs-toggle="modal" data-bs-target="#wechatModal">
|
|
<i class="fab fa-weixin me-2"></i>微信紧急联系
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 工单提交表单 -->
|
|
<div class="col-lg-8 mb-4">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">提交工单</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form id="ticketForm" class="ticket-form">
|
|
<div class="mb-3">
|
|
<label for="contactName" class="form-label">联系人姓名</label>
|
|
<input type="text" class="form-control" id="contactName" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="contactPhone" class="form-label">联系电话</label>
|
|
<input type="tel" class="form-control" id="contactPhone" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="contactEmail" class="form-label">联系邮箱</label>
|
|
<input type="email" class="form-control" id="contactEmail" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="productSelect" class="form-label">相关产品</label>
|
|
<select class="form-select" id="productSelect" required>
|
|
<option value="">请选择相关产品</option>
|
|
<option value="1">产品一</option>
|
|
<option value="2">产品二</option>
|
|
<option value="3">产品三</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="ticketTitle" class="form-label">工单标题</label>
|
|
<input type="text" class="form-control" id="ticketTitle" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="ticketContent" class="form-label">问题描述</label>
|
|
<textarea class="form-control" id="ticketContent" rows="5" required></textarea>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="prioritySelect" class="form-label">优先级</label>
|
|
<select class="form-select" id="prioritySelect">
|
|
<option value="1">低</option>
|
|
<option value="2" selected>中</option>
|
|
<option value="3">高</option>
|
|
<option value="4">紧急</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary w-100" id="submitTicketBtn">
|
|
<i class="fas fa-paper-plane me-2"></i>提交工单
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 工单历史 -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">工单历史</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="ticketsContainer">
|
|
<!-- 工单列表将通过JavaScript动态加载 -->
|
|
<div class="text-center py-5">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">加载中...</span>
|
|
</div>
|
|
<p class="mt-2">正在加载工单历史...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 微信二维码模态框 -->
|
|
<div class="modal fade" id="wechatModal" tabindex="-1" aria-labelledby="wechatModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="wechatModalLabel">紧急联系</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body text-center">
|
|
<p>扫码添加微信,快速响应紧急需求</p>
|
|
<img src="/static/images/wechat-qrcode.jpg" alt="微信二维码" class="img-fluid mb-3" style="max-width: 200px;">
|
|
<p class="text-muted small">微信ID: TaiYiSupport</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
// 售后服务页面JavaScript
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log('售后服务页面加载完成');
|
|
|
|
// 初始化产品列表
|
|
loadProducts();
|
|
|
|
// 初始化工单历史
|
|
loadTickets();
|
|
|
|
// 工单提交表单事件
|
|
document.getElementById('ticketForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
submitTicket();
|
|
});
|
|
});
|
|
|
|
// 提交工单
|
|
function submitTicket() {
|
|
const formData = {
|
|
contact_name: document.getElementById('contactName').value.trim(),
|
|
contact_phone: document.getElementById('contactPhone').value.trim(),
|
|
contact_email: document.getElementById('contactEmail').value.trim(),
|
|
product_id: document.getElementById('productSelect').value,
|
|
title: document.getElementById('ticketTitle').value.trim(),
|
|
content: document.getElementById('ticketContent').value.trim(),
|
|
priority: document.getElementById('prioritySelect').value
|
|
};
|
|
|
|
// 基础验证
|
|
if (!formData.contact_name || !formData.contact_phone || !formData.contact_email ||
|
|
!formData.product_id || !formData.title || !formData.content) {
|
|
showNotification('请填写所有必填字段', 'warning');
|
|
return;
|
|
}
|
|
|
|
// 验证手机号格式
|
|
const phoneRegex = /^1[3-9]\d{9}$/;
|
|
if (!phoneRegex.test(formData.contact_phone)) {
|
|
showNotification('请输入正确的手机号码', 'warning');
|
|
return;
|
|
}
|
|
|
|
// 验证邮箱格式
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(formData.contact_email)) {
|
|
showNotification('请输入正确的邮箱地址', 'warning');
|
|
return;
|
|
}
|
|
|
|
// 显示加载状态
|
|
const submitBtn = document.getElementById('submitTicketBtn');
|
|
const originalText = submitBtn.innerHTML;
|
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>提交中...';
|
|
submitBtn.disabled = true;
|
|
|
|
// 调用API提交工单
|
|
apiRequest('/api/v1/user/tickets', {
|
|
method: 'POST',
|
|
body: JSON.stringify(formData)
|
|
})
|
|
.then(data => {
|
|
if (data.success) {
|
|
showNotification('工单提交成功!', 'success');
|
|
document.getElementById('ticketForm').reset();
|
|
loadTickets(); // 重新加载工单列表
|
|
} else {
|
|
showNotification('工单提交失败: ' + data.message, 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showNotification('工单提交失败,请稍后重试', 'error');
|
|
console.error('提交工单失败:', error);
|
|
})
|
|
.finally(() => {
|
|
submitBtn.innerHTML = originalText;
|
|
submitBtn.disabled = false;
|
|
});
|
|
}
|
|
|
|
// 加载工单历史
|
|
function loadTickets() {
|
|
// 获取手机号
|
|
const phone = document.getElementById('contactPhone').value.trim();
|
|
|
|
// 如果没有输入手机号,不加载工单列表
|
|
if (!phone) {
|
|
document.getElementById('ticketsContainer').innerHTML = `
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-info-circle fa-2x text-muted mb-3"></i>
|
|
<h4 class="text-muted">请输入手机号查看工单历史</h4>
|
|
<p class="text-muted">输入手机号后将自动显示相关工单记录</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
// 验证手机号格式
|
|
const phoneRegex = /^1[3-9]\d{9}$/;
|
|
if (!phoneRegex.test(phone)) {
|
|
document.getElementById('ticketsContainer').innerHTML = `
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-exclamation-circle fa-2x text-warning mb-3"></i>
|
|
<h4 class="text-warning">手机号格式不正确</h4>
|
|
<p class="text-muted">请输入正确的11位手机号码</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
// 显示加载状态
|
|
document.getElementById('ticketsContainer').innerHTML = `
|
|
<div class="text-center py-5">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">加载中...</span>
|
|
</div>
|
|
<p class="mt-2">正在加载工单历史...</p>
|
|
</div>
|
|
`;
|
|
|
|
// 调用API获取工单列表
|
|
apiRequest(`/api/v1/user/tickets?phone=${phone}`)
|
|
.then(data => {
|
|
if (data.success) {
|
|
renderTickets(data.data);
|
|
} else {
|
|
showNotification('加载工单历史失败: ' + data.message, 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showNotification('加载工单历史失败,请稍后重试', 'error');
|
|
console.error('加载工单历史失败:', error);
|
|
});
|
|
}
|
|
|
|
// 渲染工单列表
|
|
function renderTickets(tickets) {
|
|
const container = document.getElementById('ticketsContainer');
|
|
|
|
if (tickets.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-ticket-alt fa-3x text-muted mb-3"></i>
|
|
<h4 class="text-muted">暂无工单记录</h4>
|
|
<p class="text-muted">您还没有提交过工单</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
let ticketsHtml = '';
|
|
tickets.forEach(ticket => {
|
|
const statusClass = ticket.status === 1 ? 'ticket-status-open' : 'ticket-status-closed';
|
|
const statusText = ticket.status === 1 ? '处理中' : '已关闭';
|
|
const priorityText = ['未知', '低', '中', '高', '紧急'][ticket.priority] || '未知';
|
|
|
|
ticketsHtml += `
|
|
<div class="ticket-item ${statusClass}">
|
|
<div class="row">
|
|
<div class="col-md-9">
|
|
<h5 class="mb-2">${ticket.title}</h5>
|
|
<p class="mb-2">${ticket.description.substring(0, 100)}${ticket.description.length > 100 ? '...' : ''}</p>
|
|
<div class="d-flex flex-wrap gap-3 text-muted">
|
|
<small><i class="fas fa-calendar me-1"></i> ${formatDate(ticket.create_time)}</small>
|
|
<small><i class="fas fa-user me-1"></i> ${ticket.contact_person}</small>
|
|
<small><i class="fas fa-box me-1"></i> ${ticket.product_name || '未知产品'}</small>
|
|
<small><i class="fas fa-exclamation-circle me-1"></i> ${priorityText}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3 text-md-end">
|
|
<span class="badge ${ticket.status === 1 ? 'bg-info' : 'bg-success'}">
|
|
${statusText}
|
|
</span>
|
|
<div class="mt-2">
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewTicket('${ticket.ticket_number}')">
|
|
<i class="fas fa-eye me-1"></i>查看
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
container.innerHTML = ticketsHtml;
|
|
}
|
|
|
|
// 查看工单详情
|
|
function viewTicket(ticketId) {
|
|
showNotification('查看工单详情功能待实现', 'info');
|
|
// 这里可以跳转到工单详情页面或显示模态框
|
|
}
|
|
|
|
// 加载产品列表
|
|
function loadProducts() {
|
|
// 调用API获取产品列表
|
|
apiRequest('/api/v1/user/products')
|
|
.then(data => {
|
|
if (data.success) {
|
|
renderProducts(data.data.products || []);
|
|
} else {
|
|
showNotification('加载产品列表失败: ' + data.message, 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showNotification('加载产品列表失败,请稍后重试', 'error');
|
|
console.error('加载产品列表失败:', error);
|
|
});
|
|
}
|
|
|
|
// 渲染产品列表
|
|
function renderProducts(products) {
|
|
const select = document.getElementById('productSelect');
|
|
|
|
// 清空现有选项(保留默认选项)
|
|
select.innerHTML = '<option value="">请选择相关产品</option>';
|
|
|
|
// 添加产品选项
|
|
products.forEach(product => {
|
|
const option = document.createElement('option');
|
|
option.value = product.product_id;
|
|
option.textContent = product.product_name;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
// 监听手机号输入框变化,自动加载工单历史
|
|
document.getElementById('contactPhone').addEventListener('input', function() {
|
|
// 延迟加载,避免频繁请求
|
|
clearTimeout(this.loadTimer);
|
|
this.loadTimer = setTimeout(() => {
|
|
loadTickets();
|
|
}, 500);
|
|
});
|
|
</script>
|
|
{% endblock %} |