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.create_version') }}" class="btn btn-primary">
|
|
|
|
|
|
<i class="fas fa-plus me-2"></i>
|
|
|
|
|
|
发布版本
|
|
|
|
|
|
</a>
|
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
|
|
{% block content %}
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<!-- 搜索表单 -->
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<div class="card shadow mb-4">
|
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
|
<form id="search-form" class="row g-3">
|
|
|
|
|
|
<div class="col-md-4">
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<label for="search-keyword" class="form-label">关键词搜索</label>
|
|
|
|
|
|
<input type="text" class="form-control" id="search-keyword" placeholder="版本号">
|
2025-11-11 21:39:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="col-md-3">
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<label for="search-product" class="form-label">产品</label>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<select class="form-select" id="search-product">
|
|
|
|
|
|
<option value="">全部产品</option>
|
|
|
|
|
|
{% for product in products %}
|
|
|
|
|
|
<option value="{{ product.product_id }}">{{ product.product_name }}</option>
|
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="col-md-3">
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<label for="search-status" class="form-label">状态</label>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<select class="form-select" id="search-status">
|
|
|
|
|
|
<option value="">全部状态</option>
|
|
|
|
|
|
<option value="0">未发布</option>
|
|
|
|
|
|
<option value="1">已发布</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<div class="col-md-2 d-flex align-items-end">
|
|
|
|
|
|
<div class="btn-group" role="group">
|
|
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
|
|
|
|
<i class="fas fa-search me-2"></i>搜索
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button type="button" class="btn btn-outline-secondary" id="reset-search">
|
|
|
|
|
|
<i class="fas fa-undo me-2"></i>重置
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<!-- 批量操作栏 -->
|
|
|
|
|
|
<div class="card shadow mb-4">
|
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<button type="button" class="btn btn-danger btn-sm" id="batch-delete-btn" style="display: none;">
|
|
|
|
|
|
<i class="fas fa-trash me-1"></i>批量删除
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div class="btn-group btn-group-sm" id="batch-status-btn" style="display: none;">
|
|
|
|
|
|
<button type="button" class="btn btn-outline-success" id="batch-publish-btn">
|
|
|
|
|
|
<i class="fas fa-paper-plane me-1"></i>批量发布
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button type="button" class="btn btn-outline-secondary" id="batch-unpublish-btn">
|
|
|
|
|
|
<i class="fas fa-undo me-1"></i>批量取消发布
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-muted small">
|
|
|
|
|
|
<span id="selected-count">已选择 0 项</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<!-- 版本列表 -->
|
|
|
|
|
|
<div class="card shadow">
|
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
|
<div class="table-responsive">
|
|
|
|
|
|
<table class="table table-hover">
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<thead class="table-light">
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<tr>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<th width="50">
|
|
|
|
|
|
<input type="checkbox" id="select-all-checkbox" class="form-check-input">
|
|
|
|
|
|
</th>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<th>版本号</th>
|
|
|
|
|
|
<th>产品</th>
|
|
|
|
|
|
<th>描述</th>
|
|
|
|
|
|
<th>文件大小</th>
|
|
|
|
|
|
<th>状态</th>
|
|
|
|
|
|
<th>发布时间</th>
|
|
|
|
|
|
<th>操作</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody id="version-list">
|
|
|
|
|
|
<tr>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<td colspan="8" class="text-center text-muted">加载中...</td>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
</tr>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<!-- 分页 -->
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<nav aria-label="分页导航">
|
|
|
|
|
|
<ul class="pagination justify-content-center mb-0" id="pagination">
|
2025-11-11 21:39:12 +08:00
|
|
|
|
</ul>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 批量删除确认模态框 -->
|
|
|
|
|
|
<div class="modal fade" id="batchDeleteModal" tabindex="-1">
|
|
|
|
|
|
<div class="modal-dialog">
|
|
|
|
|
|
<div class="modal-content">
|
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
|
<h5 class="modal-title">确认批量删除</h5>
|
|
|
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
|
确定要删除选中的 <strong id="batch-delete-count"></strong> 个版本吗?此操作不可恢复。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="modal-footer">
|
|
|
|
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
|
|
|
|
<button type="button" class="btn btn-danger" id="confirm-batch-delete">确定删除</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
2025-11-15 23:57:05 +08:00
|
|
|
|
{% block extra_js %}
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<script>
|
|
|
|
|
|
let currentPage = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 页面加载完成后初始化
|
2025-11-15 23:57:05 +08:00
|
|
|
|
// 使用立即执行函数确保在DOM和所有脚本加载完成后执行
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
function init() {
|
|
|
|
|
|
console.log('版本列表页面已加载,开始初始化...');
|
|
|
|
|
|
console.log('apiRequest函数是否存在:', typeof apiRequest);
|
|
|
|
|
|
if (typeof apiRequest === 'function') {
|
|
|
|
|
|
loadVersions();
|
|
|
|
|
|
initEventListeners();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error('apiRequest函数未定义,等待脚本加载...');
|
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
|
if (typeof apiRequest === 'function') {
|
|
|
|
|
|
loadVersions();
|
|
|
|
|
|
initEventListeners();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error('apiRequest函数仍未定义,请检查脚本加载顺序');
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (document.readyState === 'loading') {
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
init();
|
|
|
|
|
|
}
|
|
|
|
|
|
})();
|
2025-11-11 21:39:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化事件监听器
|
|
|
|
|
|
function initEventListeners() {
|
|
|
|
|
|
// 搜索表单
|
|
|
|
|
|
document.getElementById('search-form').addEventListener('submit', function(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
currentPage = 1;
|
|
|
|
|
|
loadVersions();
|
|
|
|
|
|
});
|
2025-11-12 15:11:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 重置搜索
|
|
|
|
|
|
document.getElementById('reset-search').addEventListener('click', function() {
|
|
|
|
|
|
document.getElementById('search-keyword').value = '';
|
|
|
|
|
|
document.getElementById('search-product').value = '';
|
|
|
|
|
|
document.getElementById('search-status').value = '';
|
|
|
|
|
|
currentPage = 1;
|
|
|
|
|
|
loadVersions();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 批量删除确认
|
|
|
|
|
|
document.getElementById('confirm-batch-delete').addEventListener('click', function() {
|
|
|
|
|
|
batchDeleteVersions();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 全选/取消全选
|
|
|
|
|
|
document.getElementById('select-all-checkbox').addEventListener('change', function() {
|
|
|
|
|
|
const checkboxes = document.querySelectorAll('.version-checkbox');
|
|
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
|
|
|
|
checkbox.checked = this.checked;
|
|
|
|
|
|
});
|
|
|
|
|
|
updateBatchButtons();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 批量发布
|
|
|
|
|
|
document.getElementById('batch-publish-btn').addEventListener('click', function(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
batchUpdateVersionStatus(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 批量取消发布
|
|
|
|
|
|
document.getElementById('batch-unpublish-btn').addEventListener('click', function(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
batchUpdateVersionStatus(0);
|
|
|
|
|
|
});
|
2025-11-11 21:39:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载版本列表
|
|
|
|
|
|
function loadVersions(page = 1) {
|
2025-11-15 23:57:05 +08:00
|
|
|
|
console.log('loadVersions函数被调用,页码:', page);
|
2025-11-11 21:39:12 +08:00
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
|
page: page,
|
|
|
|
|
|
per_page: 10
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 添加搜索参数
|
|
|
|
|
|
const keyword = document.getElementById('search-keyword').value.trim();
|
|
|
|
|
|
const product = document.getElementById('search-product').value;
|
|
|
|
|
|
const status = document.getElementById('search-status').value;
|
|
|
|
|
|
|
|
|
|
|
|
if (keyword) params.append('keyword', keyword);
|
|
|
|
|
|
if (product) params.append('product_id', product);
|
|
|
|
|
|
if (status) params.append('status', status);
|
|
|
|
|
|
|
2025-11-15 23:57:05 +08:00
|
|
|
|
const apiUrl = `/api/v1/versions?${params}`;
|
|
|
|
|
|
console.log('准备请求API:', apiUrl);
|
|
|
|
|
|
|
|
|
|
|
|
apiRequest(apiUrl)
|
2025-11-11 21:39:12 +08:00
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
renderVersionList(data.data.versions);
|
|
|
|
|
|
renderPagination(data.data.pagination);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification(data.message || '加载版本列表失败', 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Failed to load versions:', error);
|
|
|
|
|
|
showNotification('加载版本列表失败', 'error');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染版本列表
|
|
|
|
|
|
function renderVersionList(versions) {
|
|
|
|
|
|
const tbody = document.getElementById('version-list');
|
|
|
|
|
|
|
|
|
|
|
|
if (versions.length === 0) {
|
2025-11-12 15:11:05 +08:00
|
|
|
|
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-muted">暂无数据</td></tr>';
|
2025-11-11 21:39:12 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tbody.innerHTML = versions.map(version => `
|
|
|
|
|
|
<tr>
|
2025-11-12 15:11:05 +08:00
|
|
|
|
<td>
|
|
|
|
|
|
<input type="checkbox" class="version-checkbox" data-version-id="${version.version_id}">
|
|
|
|
|
|
</td>
|
2025-11-11 21:39:12 +08:00
|
|
|
|
<td>
|
|
|
|
|
|
<strong>${version.version_num}</strong>
|
|
|
|
|
|
<br><small class="text-muted">${version.platform || '-'}</small>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
${version.product_name || '-'}
|
|
|
|
|
|
<br><small class="text-muted">${version.product_id || '-'}</small>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td>${version.description || '-'}</td>
|
|
|
|
|
|
<td>${version.file_size ? formatFileSize(version.file_size) : '-'}</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<span class="badge ${version.publish_status === 1 ? 'bg-success' : 'bg-secondary'}">
|
|
|
|
|
|
${version.publish_status === 1 ? '已发布' : '未发布'}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<small>${version.publish_time ? formatDate(version.publish_time) : '-'}</small>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
|
|
|
|
<a href="/versions/${version.version_id}" class="btn btn-outline-primary" title="查看">
|
|
|
|
|
|
<i class="fas fa-eye"></i>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<a href="/versions/${version.version_id}/edit" class="btn btn-outline-secondary" title="编辑">
|
|
|
|
|
|
<i class="fas fa-edit"></i>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
${version.publish_status === 0 || version.publish_status === 2 ? `
|
|
|
|
|
|
<button type="button" class="btn btn-outline-success btn-publish"
|
|
|
|
|
|
data-version-id="${version.version_id}"
|
|
|
|
|
|
title="发布">
|
|
|
|
|
|
<i class="fas fa-paper-plane"></i>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
${version.publish_status === 0 ? `
|
|
|
|
|
|
<button type="button" class="btn btn-outline-danger btn-delete"
|
|
|
|
|
|
data-version-id="${version.version_id}"
|
|
|
|
|
|
title="删除">
|
|
|
|
|
|
<i class="fas fa-trash"></i>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
` : ''}
|
|
|
|
|
|
` : `
|
|
|
|
|
|
<button type="button" class="btn btn-outline-warning btn-unpublish"
|
|
|
|
|
|
data-version-id="${version.version_id}"
|
|
|
|
|
|
title="取消发布">
|
|
|
|
|
|
<i class="fas fa-undo"></i>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
`}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
`).join('');
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
document.querySelectorAll('.btn-publish').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', function() {
|
|
|
|
|
|
const versionId = this.dataset.versionId;
|
|
|
|
|
|
updateVersionStatus(versionId, 1); // 发布
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.btn-unpublish').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', function() {
|
|
|
|
|
|
const versionId = this.dataset.versionId;
|
|
|
|
|
|
updateVersionStatus(versionId, 0); // 取消发布
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.btn-delete').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', function() {
|
|
|
|
|
|
const versionId = this.dataset.versionId;
|
|
|
|
|
|
deleteVersion(versionId);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-11-12 15:11:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 绑定复选框事件
|
|
|
|
|
|
document.querySelectorAll('.version-checkbox').forEach(checkbox => {
|
|
|
|
|
|
checkbox.addEventListener('change', updateBatchButtons);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 重置全选复选框
|
|
|
|
|
|
document.getElementById('select-all-checkbox').checked = false;
|
|
|
|
|
|
updateBatchButtons();
|
2025-11-11 21:39:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染分页
|
|
|
|
|
|
function renderPagination(pagination) {
|
|
|
|
|
|
const paginationEl = document.getElementById('pagination');
|
|
|
|
|
|
|
|
|
|
|
|
if (pagination.pages <= 1) {
|
|
|
|
|
|
paginationEl.innerHTML = '';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let html = '';
|
|
|
|
|
|
|
|
|
|
|
|
// 上一页
|
|
|
|
|
|
if (pagination.has_prev) {
|
|
|
|
|
|
html += `<li class="page-item">
|
|
|
|
|
|
<a class="page-link" href="#" data-page="${pagination.page - 1}">上一页</a>
|
|
|
|
|
|
</li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 页码
|
|
|
|
|
|
const startPage = Math.max(1, pagination.page - 2);
|
|
|
|
|
|
const endPage = Math.min(pagination.pages, pagination.page + 2);
|
|
|
|
|
|
|
|
|
|
|
|
if (startPage > 1) {
|
|
|
|
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="1">1</a></li>`;
|
|
|
|
|
|
if (startPage > 2) {
|
|
|
|
|
|
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
|
|
|
|
html += `<li class="page-item ${i === pagination.page ? 'active' : ''}">
|
|
|
|
|
|
<a class="page-link" href="#" data-page="${i}">${i}</a>
|
|
|
|
|
|
</li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (endPage < pagination.pages) {
|
|
|
|
|
|
if (endPage < pagination.pages - 1) {
|
|
|
|
|
|
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="${pagination.pages}">${pagination.pages}</a></li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 下一页
|
|
|
|
|
|
if (pagination.has_next) {
|
|
|
|
|
|
html += `<li class="page-item">
|
|
|
|
|
|
<a class="page-link" href="#" data-page="${pagination.page + 1}">下一页</a>
|
|
|
|
|
|
</li>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
paginationEl.innerHTML = html;
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定分页点击事件
|
|
|
|
|
|
paginationEl.querySelectorAll('.page-link').forEach(link => {
|
|
|
|
|
|
link.addEventListener('click', function(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
const page = parseInt(this.dataset.page);
|
|
|
|
|
|
if (page && page !== currentPage) {
|
|
|
|
|
|
currentPage = page;
|
|
|
|
|
|
loadVersions(page);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新版本状态
|
|
|
|
|
|
function updateVersionStatus(versionId, status) {
|
|
|
|
|
|
const action = status === 1 ? '发布' : '取消发布';
|
|
|
|
|
|
|
|
|
|
|
|
if (!confirm(`确定要${action}该版本吗?`)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
apiRequest(`/api/v1/versions/${versionId}/status`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
body: JSON.stringify({ status: status })
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
showNotification(`${action}成功`, 'success');
|
|
|
|
|
|
loadVersions(currentPage);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification(data.message || `${action}失败`, 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Failed to update version status:', error);
|
|
|
|
|
|
showNotification(`${action}失败`, 'error');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除版本
|
|
|
|
|
|
function deleteVersion(versionId) {
|
|
|
|
|
|
if (!confirm('确定要删除该版本吗?')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
apiRequest(`/api/v1/versions/${versionId}`, {
|
|
|
|
|
|
method: 'DELETE'
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
showNotification('删除成功', 'success');
|
|
|
|
|
|
loadVersions(currentPage);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification(data.message || '删除失败', 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Failed to delete version:', error);
|
|
|
|
|
|
if (error.message && error.message.includes('HTTP error! status: 400')) {
|
|
|
|
|
|
showNotification('删除失败:该版本可能已发布或正在被设备使用,请先取消发布并确保无设备使用后再删除', 'error');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification('删除失败:' + (error.message || '未知错误'), 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-11-12 15:11:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新批量操作按钮状态
|
|
|
|
|
|
function updateBatchButtons() {
|
|
|
|
|
|
const selectedCount = document.querySelectorAll('.version-checkbox:checked').length;
|
|
|
|
|
|
const batchDeleteBtn = document.getElementById('batch-delete-btn');
|
|
|
|
|
|
const batchStatusBtn = document.getElementById('batch-status-btn');
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedCount > 0) {
|
|
|
|
|
|
batchDeleteBtn.style.display = 'inline-block';
|
|
|
|
|
|
batchStatusBtn.style.display = 'inline-block';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
batchDeleteBtn.style.display = 'none';
|
|
|
|
|
|
batchStatusBtn.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新选中数量显示
|
|
|
|
|
|
document.getElementById('selected-count').textContent = `已选择 ${selectedCount} 项`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示批量删除确认模态框
|
|
|
|
|
|
function showBatchDeleteModal() {
|
|
|
|
|
|
const selectedCount = document.querySelectorAll('.version-checkbox:checked').length;
|
|
|
|
|
|
document.getElementById('batch-delete-count').textContent = selectedCount;
|
|
|
|
|
|
|
|
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('batchDeleteModal'));
|
|
|
|
|
|
modal.show();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量删除版本
|
|
|
|
|
|
function batchDeleteVersions() {
|
|
|
|
|
|
const selectedCheckboxes = document.querySelectorAll('.version-checkbox:checked');
|
|
|
|
|
|
const versionIds = Array.from(selectedCheckboxes).map(checkbox => parseInt(checkbox.dataset.versionId));
|
|
|
|
|
|
|
|
|
|
|
|
apiRequest('/api/v1/versions/batch', {
|
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
|
body: JSON.stringify({ version_ids: versionIds })
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
showNotification(data.message || '批量删除成功', 'success');
|
|
|
|
|
|
loadVersions(currentPage);
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭模态框
|
|
|
|
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('batchDeleteModal'));
|
|
|
|
|
|
modal.hide();
|
|
|
|
|
|
|
|
|
|
|
|
// 重置全选复选框
|
|
|
|
|
|
document.getElementById('select-all-checkbox').checked = false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification(data.message || '批量删除失败', 'error');
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭模态框
|
|
|
|
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('batchDeleteModal'));
|
|
|
|
|
|
modal.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Failed to batch delete versions:', error);
|
|
|
|
|
|
showNotification('批量删除失败', 'error');
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭模态框
|
|
|
|
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('batchDeleteModal'));
|
|
|
|
|
|
modal.hide();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量更新版本状态
|
|
|
|
|
|
function batchUpdateVersionStatus(status) {
|
|
|
|
|
|
const selectedCheckboxes = document.querySelectorAll('.version-checkbox:checked');
|
|
|
|
|
|
const versionIds = Array.from(selectedCheckboxes).map(checkbox => parseInt(checkbox.dataset.versionId));
|
|
|
|
|
|
|
|
|
|
|
|
if (versionIds.length === 0) {
|
|
|
|
|
|
showNotification('请至少选择一个版本', 'warning');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
apiRequest('/api/v1/versions/batch/status', {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
version_ids: versionIds,
|
|
|
|
|
|
status: status
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
showNotification(data.message || '批量更新状态成功', 'success');
|
|
|
|
|
|
loadVersions(currentPage);
|
|
|
|
|
|
|
|
|
|
|
|
// 重置全选复选框
|
|
|
|
|
|
document.getElementById('select-all-checkbox').checked = false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showNotification(data.message || '批量更新状态失败', 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Failed to batch update version status:', error);
|
|
|
|
|
|
showNotification('批量更新状态失败', 'error');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在页面加载完成后为批量删除按钮绑定事件
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
const batchDeleteBtn = document.getElementById('batch-delete-btn');
|
|
|
|
|
|
if (batchDeleteBtn) {
|
|
|
|
|
|
batchDeleteBtn.addEventListener('click', showBatchDeleteModal);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-15 23:57:05 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
{% endblock %}
|