Kamixitong/app/web/templates/ticket/detail.html

311 lines
11 KiB
HTML
Raw Normal View History

2025-11-11 21:39:12 +08:00
{% extends "base.html" %}
{% block title %}工单详情 - 软件授权管理系统{% endblock %}
{% block page_title %}工单详情{% endblock %}
{% block page_actions %}
<a href="{{ url_for('web.tickets') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>
返回列表
</a>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8">
<!-- 工单基本信息 -->
<div class="card shadow mb-4">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h6 class="mb-0">工单信息</h6>
<div>
<span class="badge {{ 'bg-secondary' if ticket.status == 0 else 'bg-info' if ticket.status == 1 else 'bg-success' if ticket.status == 2 else 'bg-dark' }}">
{{ '待处理' if ticket.status == 0 else '处理中' if ticket.status == 1 else '已解决' if ticket.status == 2 else '已关闭' }}
</span>
{{ getPriorityBadge(ticket.priority) }}
</div>
</div>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-2">工单ID:</dt>
<dd class="col-sm-10"><code>{{ ticket.ticket_id }}</code></dd>
<dt class="col-sm-2">标题:</dt>
<dd class="col-sm-10">{{ ticket.title }}</dd>
<dt class="col-sm-2">关联产品:</dt>
<dd class="col-sm-10">
{% if ticket.product %}
{{ ticket.product.product_name }} ({{ ticket.product.product_id }})
{% else %}
无关联产品
{% endif %}
</dd>
<dt class="col-sm-2">创建时间:</dt>
<dd class="col-sm-10">{{ formatDate(ticket.create_time) }}</dd>
<dt class="col-sm-2">最后更新:</dt>
<dd class="col-sm-10">{{ ticket.update_time or '-' }}</dd>
<dt class="col-sm-2">创建人:</dt>
<dd class="col-sm-10">{{ ticket.creator or '系统用户' }}</dd>
</dl>
</div>
</div>
<!-- 工单内容 -->
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">工单内容</h6>
</div>
<div class="card-body">
<div class="bg-light p-3 rounded">
{{ ticket.content|safe }}
</div>
</div>
</div>
<!-- 回复列表 -->
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">回复记录 ({{ ticket.replies|length }})</h6>
</div>
<div class="card-body">
{% if ticket.replies %}
{% for reply in ticket.replies %}
<div class="border-bottom pb-3 mb-3">
<div class="d-flex justify-content-between">
<strong>{{ reply.creator or '支持团队' }}</strong>
<small class="text-muted">{{ formatDate(reply.create_time) }}</small>
</div>
<div class="mt-2">
{{ reply.content|safe }}
</div>
</div>
{% endfor %}
{% else %}
<p class="text-muted text-center">暂无回复</p>
{% endif %}
</div>
</div>
<!-- 回复表单 -->
<div class="card shadow">
<div class="card-header">
<h6 class="mb-0">添加回复</h6>
</div>
<div class="card-body">
<form id="reply-form">
<div class="mb-3">
<textarea class="form-control" id="reply_content" rows="4" placeholder="请输入回复内容..." required></textarea>
</div>
<button type="submit" class="btn btn-primary" id="reply-btn">
<i class="fas fa-paper-plane me-2"></i>
<span id="reply-text">提交回复</span>
</button>
{% if ticket.status != 3 %}
<button type="button" class="btn btn-success ms-2" id="resolve-btn">
<i class="fas fa-check me-2"></i>
标记为已解决
</button>
{% endif %}
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- 操作面板 -->
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">操作</h6>
</div>
<div class="card-body">
<div class="d-grid gap-2">
{% if ticket.status == 0 %}
<button type="button" class="btn btn-info" id="start-process-btn">
<i class="fas fa-play me-2"></i>
开始处理
</button>
{% endif %}
{% if ticket.status in [0, 1] %}
<button type="button" class="btn btn-warning" id="close-ticket-btn">
<i class="fas fa-times me-2"></i>
关闭工单
</button>
{% endif %}
{% if ticket.status == 3 %}
<button type="button" class="btn btn-success" id="reopen-ticket-btn">
<i class="fas fa-redo me-2"></i>
重新打开
</button>
{% endif %}
</div>
</div>
</div>
<!-- 工单信息 -->
<div class="card shadow">
<div class="card-header">
<h6 class="mb-0">工单信息</h6>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li class="mb-2">
<i class="fas fa-info-circle text-primary me-2"></i>
<small>工单状态会影响用户的使用体验</small>
</li>
<li class="mb-2">
<i class="fas fa-info-circle text-primary me-2"></i>
<small>请及时回复用户的问题</small>
</li>
<li class="mb-2">
<i class="fas fa-info-circle text-primary me-2"></i>
<small>工单关闭后仍可重新打开</small>
</li>
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 获取优先级徽章
function getPriorityBadge(priority) {
const priorityMap = {
0: '<span class="badge bg-secondary ms-1"></span>',
1: '<span class="badge bg-warning ms-1"></span>',
2: '<span class="badge bg-danger ms-1"></span>'
};
return priorityMap[priority] || '<span class="badge bg-secondary ms-1">未知</span>';
}
// 格式化日期
function formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN');
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initEventListeners();
});
// 初始化事件监听器
function initEventListeners() {
// 回复表单
document.getElementById('reply-form').addEventListener('submit', function(e) {
e.preventDefault();
addReply();
});
// 标记为已解决
document.getElementById('resolve-btn').addEventListener('click', function() {
updateTicketStatus(2); // 已解决
});
// 开始处理
if (document.getElementById('start-process-btn')) {
document.getElementById('start-process-btn').addEventListener('click', function() {
updateTicketStatus(1); // 处理中
});
}
// 关闭工单
document.getElementById('close-ticket-btn').addEventListener('click', function() {
if (confirm('确定要关闭该工单吗?')) {
updateTicketStatus(3); // 已关闭
}
});
// 重新打开工单
if (document.getElementById('reopen-ticket-btn')) {
document.getElementById('reopen-ticket-btn').addEventListener('click', function() {
updateTicketStatus(1); // 处理中
});
}
}
// 添加回复
function addReply() {
const replyBtn = document.getElementById('reply-btn');
const replyText = document.getElementById('reply-text');
const content = document.getElementById('reply_content').value.trim();
if (!content) {
showNotification('请输入回复内容', 'warning');
return;
}
// 显示加载状态
replyBtn.disabled = true;
replyText.textContent = '提交中...';
// 发送请求
apiRequest(`/api/v1/tickets/{{ ticket.ticket_id }}/replies`, {
method: 'POST',
body: JSON.stringify({ content: content })
})
.then(data => {
if (data.success) {
showNotification('回复成功', 'success');
// 清空输入框
document.getElementById('reply_content').value = '';
// 重新加载页面以显示新回复
setTimeout(() => {
location.reload();
}, 1000);
} else {
showNotification(data.message || '回复失败', 'error');
}
})
.catch(error => {
console.error('Failed to add reply:', error);
showNotification('网络错误,请稍后重试', 'error');
})
.finally(() => {
// 恢复按钮状态
replyBtn.disabled = false;
replyText.textContent = '提交回复';
});
}
// 更新工单状态
function updateTicketStatus(status) {
const statusText = {
0: '待处理',
1: '处理中',
2: '已解决',
3: '已关闭'
};
apiRequest(`/api/v1/tickets/{{ ticket.ticket_id }}/status`, {
method: 'PUT',
body: JSON.stringify({ status: status })
})
.then(data => {
if (data.success) {
showNotification(`工单状态已更新为${statusText[status]}`, 'success');
// 重新加载页面以显示新状态
setTimeout(() => {
location.reload();
}, 1000);
} else {
showNotification(data.message || '状态更新失败', 'error');
}
})
.catch(error => {
console.error('Failed to update ticket status:', error);
showNotification('网络错误,请稍后重试', 'error');
});
}
</script>
{% endblock %}