Kamixitong/app/web/templates/version/create.html

311 lines
12 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.versions') }}" 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">
<div class="card-body">
<form id="version-form" enctype="multipart/form-data">
<div class="mb-3">
<label for="product_id" class="form-label">选择产品 *</label>
<select class="form-select" id="product_id" name="product_id" required>
<option value="">请选择产品</option>
{% for product in products %}
<option value="{{ product.product_id }}" {% if request.args.get('product_id') == product.product_id %}selected{% endif %}>
{{ product.product_name }}
</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="version_num" class="form-label">版本号 *</label>
<input type="text" class="form-control" id="version_num" name="version_num" required>
<div class="form-text">版本号格式建议使用 x.x.x 格式,如 1.0.0</div>
</div>
<div class="mb-3">
<label for="platform" class="form-label">平台</label>
<input type="text" class="form-control" id="platform" name="platform" placeholder="如: Windows, macOS, Linux">
</div>
<div class="mb-3">
<label for="description" class="form-label">版本描述</label>
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
</div>
<div class="mb-3">
<label for="update_notes" class="form-label">更新日志</label>
<textarea class="form-control" id="update_notes" name="update_notes" rows="5"></textarea>
</div>
<!-- 文件上传区域 -->
<div class="mb-3">
<label class="form-label">版本文件上传</label>
<div class="border rounded p-3 mb-2">
<div class="d-flex justify-content-between align-items-center mb-2">
<button type="button" class="btn btn-outline-primary btn-sm" id="upload-file-btn">
<i class="fas fa-upload me-1"></i>选择文件
</button>
<input type="file" id="version_file" name="version_file" accept=".zip,.rar,.7z,.exe,.dmg,.pkg" class="d-none">
<div id="upload-status" class="text-muted">未选择文件</div>
</div>
<div id="file-info" class="small text-muted d-none">
<div>文件名: <span id="file-name"></span></div>
<div>文件大小: <span id="file-size"></span></div>
<div>文件哈希: <span id="file-hash"></span></div>
<input type="hidden" id="file_url" name="file_url">
<input type="hidden" id="file_hash" name="file_hash">
</div>
</div>
<div class="form-text">支持常见的压缩包和安装包格式,文件将单独上传</div>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="publish_now" name="publish_now">
<label class="form-check-label" for="publish_now">立即发布</label>
</div>
<button type="submit" class="btn btn-primary" id="submit-btn">
<i class="fas fa-save me-2"></i>
<span id="submit-text">创建版本</span>
</button>
<a href="{{ url_for('web.versions') }}" class="btn btn-secondary">取消</a>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<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>
<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>
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initEventListeners();
});
// 初始化事件监听器
function initEventListeners() {
// 表单提交
document.getElementById('version-form').addEventListener('submit', function(e) {
e.preventDefault();
createVersion();
});
// 文件选择按钮点击事件
document.getElementById('upload-file-btn').addEventListener('click', function() {
document.getElementById('version_file').click();
});
// 文件选择变化事件
document.getElementById('version_file').addEventListener('change', function(e) {
if (e.target.files.length > 0) {
uploadFile(e.target.files[0]);
}
});
}
// 上传文件
function uploadFile(file) {
const uploadBtn = document.getElementById('upload-file-btn');
const uploadStatus = document.getElementById('upload-status');
// 显示上传状态
uploadBtn.disabled = true;
uploadStatus.textContent = '上传中...';
// 创建FormData对象
const formData = new FormData();
formData.append('version_file', file);
// 发送上传请求
fetch('/api/v1/versions/upload', {
method: 'POST',
body: formData,
credentials: 'same-origin'
})
.then(response => {
console.log('Upload response status:', response.status);
console.log('Upload response headers:', [...response.headers.entries()]);
if (response.ok) {
return response.json();
} else {
// 尝试获取错误响应体
return response.text().then(text => {
console.error('Upload error response body:', text);
throw new Error(`HTTP error! status: ${response.status}, body: ${text}`);
});
}
})
.then(data => {
console.log('Upload response data:', data);
if (data.success) {
// 显示文件信息
document.getElementById('file-name').textContent = data.data.file_name;
document.getElementById('file-size').textContent = formatFileSize(data.data.file_size);
document.getElementById('file-hash').textContent = data.data.file_hash;
document.getElementById('file_url').value = data.data.file_url;
document.getElementById('file_hash').value = data.data.file_hash;
// 显示文件信息区域
document.getElementById('file-info').classList.remove('d-none');
uploadStatus.textContent = '上传成功';
} else {
uploadStatus.textContent = data.message || '上传失败';
showNotification(data.message || '文件上传失败', 'error');
}
})
.catch(error => {
console.error('文件上传失败:', error);
uploadStatus.textContent = '上传失败';
showNotification('文件上传失败,请稍后重试: ' + error.message, 'error');
})
.finally(() => {
// 恢复按钮状态
uploadBtn.disabled = false;
});
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 创建版本
function createVersion() {
const submitBtn = document.getElementById('submit-btn');
const submitText = document.getElementById('submit-text');
// 获取表单数据
const formData = new FormData();
formData.append('product_id', document.getElementById('product_id').value);
formData.append('version_num', document.getElementById('version_num').value.trim());
const platform = document.getElementById('platform').value.trim();
if (platform) {
formData.append('platform', platform);
}
const description = document.getElementById('description').value.trim();
if (description) {
formData.append('description', description);
}
const updateNotes = document.getElementById('update_notes').value.trim();
if (updateNotes) {
formData.append('update_notes', updateNotes);
}
// 添加文件信息(如果已上传)
const fileUrl = document.getElementById('file_url').value;
if (fileUrl) {
formData.append('download_url', fileUrl);
}
const fileHash = document.getElementById('file_hash').value;
if (fileHash) {
formData.append('file_hash', fileHash);
}
const publishNow = document.getElementById('publish_now').checked;
formData.append('publish_now', publishNow);
// 基础验证
if (!document.getElementById('product_id').value) {
showNotification('请选择产品', 'warning');
return;
}
if (!document.getElementById('version_num').value.trim()) {
showNotification('请输入版本号', 'warning');
return;
}
// 显示加载状态
submitBtn.disabled = true;
submitText.textContent = '创建中...';
// 发送请求
showLoading();
fetch('/api/v1/versions', {
method: 'POST',
body: formData,
credentials: 'same-origin'
})
.then(response => {
hideLoading();
if (response.ok) {
return response.json();
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
})
.then(data => {
if (data.success) {
showNotification('版本创建成功', 'success');
// 延迟跳转到版本列表
setTimeout(() => {
window.location.href = '/versions';
}, 1500);
} else {
showNotification(data.message || '创建失败', 'error');
}
})
.catch(error => {
hideLoading();
console.error('Failed to create version:', error);
showNotification('网络错误,请稍后重试', 'error');
})
.finally(() => {
// 恢复按钮状态
submitBtn.disabled = false;
submitText.textContent = '创建版本';
});
}
</script>
{% endblock %}