Files
abot/admin/dashboard/templates/message_push_management.html
2025-06-10 11:24:08 +08:00

439 lines
17 KiB
HTML

{% extends "base.html" %}
{% block title %}消息推送管理{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">消息推送管理</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#createTaskModal">
<i class="fas fa-plus"></i> 新建任务
</button>
</div>
</div>
<div class="card-body">
<!-- 搜索栏 -->
<div class="row mb-3">
<div class="col-md-3">
<select class="form-control" id="statusFilter">
<option value="">全部状态</option>
<option value="draft">草稿</option>
<option value="scheduled">已排期</option>
<option value="paused">已暂停</option>
<option value="completed">已完成</option>
</select>
</div>
<div class="col-md-3">
<input type="date" class="form-control" id="startTimeFilter" placeholder="开始时间">
</div>
<div class="col-md-3">
<input type="date" class="form-control" id="endTimeFilter" placeholder="结束时间">
</div>
<div class="col-md-3">
<button type="button" class="btn btn-info" id="searchBtn">
<i class="fas fa-search"></i> 搜索
</button>
<button type="button" class="btn btn-secondary" id="resetBtn">
<i class="fas fa-redo"></i> 重置
</button>
</div>
</div>
<!-- 任务列表 -->
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>任务ID</th>
<th>任务名称</th>
<th>状态</th>
<th>计划时间</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="taskList">
<!-- 任务列表将通过JavaScript动态加载 -->
</tbody>
</table>
<!-- 分页 -->
<div class="row">
<div class="col-md-6">
<div class="dataTables_info" id="taskInfo" role="status" aria-live="polite">
显示 0 到 0 条,共 0 条记录
</div>
</div>
<div class="col-md-6">
<div class="dataTables_paginate paging_simple_numbers" id="taskPagination">
<!-- 分页将通过JavaScript动态加载 -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 新建任务模态框 -->
<div class="modal fade" id="createTaskModal" tabindex="-1" role="dialog" aria-labelledby="createTaskModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createTaskModalLabel">新建任务</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="createTaskForm">
<div class="form-group">
<label for="taskName">任务名称</label>
<input type="text" class="form-control" id="taskName" name="task_name" required>
</div>
<div class="form-group">
<label for="scheduleTime">计划时间</label>
<input type="datetime-local" class="form-control" id="scheduleTime" name="schedule_time" required>
</div>
<div class="form-group">
<label for="groups">目标群组</label>
<select class="form-control" id="groups" name="groups" multiple required>
<!-- 群组列表将通过JavaScript动态加载 -->
</select>
</div>
<div class="form-group">
<label for="contentText">文本内容</label>
<textarea class="form-control" id="contentText" name="content_text" rows="3"></textarea>
</div>
<div class="form-group">
<label for="contentImage">图片内容</label>
<input type="file" class="form-control-file" id="contentImage" name="content_image">
</div>
<div class="form-group">
<label for="contentLink">链接内容</label>
<input type="url" class="form-control" id="contentLink" name="content_link">
</div>
<div class="form-group">
<label for="contentMiniprogram">小程序内容</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="miniprogramTitle" name="miniprogram_title" placeholder="标题">
</div>
<div class="col-md-6">
<input type="text" class="form-control" id="miniprogramPath" name="miniprogram_path" placeholder="路径">
</div>
</div>
</div>
<div class="form-group">
<label for="previewRecipients">预览接收人</label>
<select class="form-control" id="previewRecipients" name="preview_recipients" multiple>
<!-- 用户列表将通过JavaScript动态加载 -->
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="saveTaskBtn">保存</button>
</div>
</div>
</div>
</div>
<!-- 任务日志模态框 -->
<div class="modal fade" id="taskLogsModal" tabindex="-1" role="dialog" aria-labelledby="taskLogsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="taskLogsModalLabel">任务日志</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<table class="table table-bordered">
<thead>
<tr>
<th>时间</th>
<th>操作</th>
<th>操作人</th>
<th>变更内容</th>
</tr>
</thead>
<tbody id="taskLogsList">
<!-- 日志列表将通过JavaScript动态加载 -->
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
// 全局变量
let currentPage = 1;
let pageSize = 20;
let totalPages = 0;
// 加载任务列表
function loadTasks(page = 1) {
const status = $('#statusFilter').val();
const startTime = $('#startTimeFilter').val();
const endTime = $('#endTimeFilter').val();
$.get('/message_push/api/tasks', {
page: page,
limit: pageSize,
status: status,
start_time: startTime,
end_time: endTime
}, function(response) {
if (response.success) {
const { tasks, total } = response.data;
totalPages = Math.ceil(total / pageSize);
// 渲染任务列表
let html = '';
tasks.forEach(task => {
html += `
<tr>
<td>${task.task_id}</td>
<td>${task.task_name}</td>
<td>${getStatusText(task.status)}</td>
<td>${formatDateTime(task.schedule_time)}</td>
<td>${formatDateTime(task.created_at)}</td>
<td>
<button class="btn btn-sm btn-info" onclick="previewTask('${task.task_id}')">
<i class="fas fa-eye"></i> 预览
</button>
<button class="btn btn-sm btn-warning" onclick="editTask('${task.task_id}')">
<i class="fas fa-edit"></i> 编辑
</button>
${task.status === 'scheduled' ? `
<button class="btn btn-sm btn-secondary" onclick="pauseTask('${task.task_id}')">
<i class="fas fa-pause"></i> 暂停
</button>
` : task.status === 'paused' ? `
<button class="btn btn-sm btn-success" onclick="resumeTask('${task.task_id}')">
<i class="fas fa-play"></i> 恢复
</button>
` : ''}
<button class="btn btn-sm btn-danger" onclick="deleteTask('${task.task_id}')">
<i class="fas fa-trash"></i> 删除
</button>
<button class="btn btn-sm btn-primary" onclick="viewLogs('${task.task_id}')">
<i class="fas fa-history"></i> 日志
</button>
</td>
</tr>
`;
});
$('#taskList').html(html);
// 更新分页信息
updatePagination(page, total);
} else {
showError('加载任务列表失败:' + response.error);
}
});
}
// 更新分页信息
function updatePagination(currentPage, total) {
// 更新信息文本
const start = (currentPage - 1) * pageSize + 1;
const end = Math.min(currentPage * pageSize, total);
$('#taskInfo').text(`显示 ${start}${end} 条,共 ${total} 条记录`);
// 更新分页按钮
let html = '<ul class="pagination">';
// 上一页
html += `
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
<a class="page-link" href="#" onclick="return loadTasks(${currentPage - 1})">上一页</a>
</li>
`;
// 页码
for (let i = 1; i <= totalPages; i++) {
if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) {
html += `
<li class="page-item ${i === currentPage ? 'active' : ''}">
<a class="page-link" href="#" onclick="return loadTasks(${i})">${i}</a>
</li>
`;
} else if (i === currentPage - 3 || i === currentPage + 3) {
html += '<li class="page-item disabled"><a class="page-link">...</a></li>';
}
}
// 下一页
html += `
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
<a class="page-link" href="#" onclick="return loadTasks(${currentPage + 1})">下一页</a>
</li>
`;
html += '</ul>';
$('#taskPagination').html(html);
}
// 工具函数
function getStatusText(status) {
const statusMap = {
'draft': '草稿',
'scheduled': '已排期',
'paused': '已暂停',
'completed': '已完成'
};
return statusMap[status] || status;
}
function formatDateTime(datetime) {
return new Date(datetime).toLocaleString();
}
function showError(message) {
toastr.error(message);
}
function showSuccess(message) {
toastr.success(message);
}
// 事件处理
$('#searchBtn').click(function() {
loadTasks(1);
});
$('#resetBtn').click(function() {
$('#statusFilter').val('');
$('#startTimeFilter').val('');
$('#endTimeFilter').val('');
loadTasks(1);
});
$('#saveTaskBtn').click(function() {
const formData = new FormData($('#createTaskForm')[0]);
const data = {};
formData.forEach((value, key) => {
if (key === 'groups' || key === 'preview_recipients') {
data[key] = Array.from($(`#${key}`).val());
} else {
data[key] = value;
}
});
// 处理小程序内容
data.content_miniprogram = {
title: $('#miniprogramTitle').val(),
path: $('#miniprogramPath').val()
};
$.ajax({
url: '/message_push/api/tasks',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(response) {
if (response.success) {
showSuccess('创建任务成功');
$('#createTaskModal').modal('hide');
loadTasks(1);
} else {
showError('创建任务失败:' + response.error);
}
}
});
});
// 初始化
loadTasks(1);
});
// 任务操作函数
function previewTask(taskId) {
$.post(`/message_push/api/tasks/${taskId}/preview`, function(response) {
if (response.success) {
showSuccess('预览已发送');
} else {
showError('发送预览失败:' + response.error);
}
});
}
function editTask(taskId) {
// TODO: 实现编辑任务功能
}
function pauseTask(taskId) {
$.post(`/message_push/api/tasks/${taskId}/pause`, function(response) {
if (response.success) {
showSuccess('任务已暂停');
loadTasks(currentPage);
} else {
showError('暂停任务失败:' + response.error);
}
});
}
function resumeTask(taskId) {
$.post(`/message_push/api/tasks/${taskId}/resume`, function(response) {
if (response.success) {
showSuccess('任务已恢复');
loadTasks(currentPage);
} else {
showError('恢复任务失败:' + response.error);
}
});
}
function deleteTask(taskId) {
if (confirm('确定要删除这个任务吗?')) {
$.ajax({
url: `/message_push/api/tasks/${taskId}`,
type: 'DELETE',
success: function(response) {
if (response.success) {
showSuccess('任务已删除');
loadTasks(currentPage);
} else {
showError('删除任务失败:' + response.error);
}
}
});
}
}
function viewLogs(taskId) {
$.get(`/message_push/api/tasks/${taskId}/logs`, function(response) {
if (response.success) {
const { logs } = response.data;
let html = '';
logs.forEach(log => {
html += `
<tr>
<td>${formatDateTime(log.timestamp)}</td>
<td>${log.action}</td>
<td>${log.operator_id}</td>
<td>${JSON.stringify(log.changes)}</td>
</tr>
`;
});
$('#taskLogsList').html(html);
$('#taskLogsModal').modal('show');
} else {
showError('获取任务日志失败:' + response.error);
}
});
}
</script>
{% endblock %}