Kamixitong/app/web/templates/version/list.html
2025-11-11 21:39:12 +08:00

337 lines
11 KiB
HTML

{% 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 %}
<!-- 搜索和筛选 -->
<div class="card shadow mb-4">
<div class="card-body">
<form id="search-form" class="row g-3">
<div class="col-md-4">
<input type="text" class="form-control" id="search-keyword" placeholder="搜索版本号或描述...">
</div>
<div class="col-md-3">
<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">
<select class="form-select" id="search-status">
<option value="">全部状态</option>
<option value="0">未发布</option>
<option value="1">已发布</option>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-outline-primary">
<i class="fas fa-search me-2"></i>
搜索
</button>
</div>
</form>
</div>
</div>
<!-- 版本列表 -->
<div class="card shadow">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>版本号</th>
<th>产品</th>
<th>描述</th>
<th>文件大小</th>
<th>状态</th>
<th>发布时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="version-list">
<tr>
<td colspan="7" class="text-center text-muted">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
加载中...
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<nav aria-label="版本列表分页">
<ul class="pagination justify-content-center" id="pagination">
<!-- 分页将通过JavaScript动态生成 -->
</ul>
</nav>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let currentPage = 1;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
loadVersions();
initEventListeners();
});
// 初始化事件监听器
function initEventListeners() {
// 搜索表单
document.getElementById('search-form').addEventListener('submit', function(e) {
e.preventDefault();
currentPage = 1;
loadVersions();
});
}
// 加载版本列表
function loadVersions(page = 1) {
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);
apiRequest(`/api/v1/versions?${params}`)
.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) {
tbody.innerHTML = '<tr><td colspan="7" class="text-center text-muted">暂无数据</td></tr>';
return;
}
tbody.innerHTML = versions.map(version => `
<tr>
<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);
});
});
}
// 渲染分页
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');
}
});
}
</script>
{% endblock %}