Kamixitong/app/web/templates/license/import.html

279 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.licenses') }}" 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="import-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 }}">{{ product.product_name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="license_file" class="form-label">选择文件 *</label>
<input type="file" class="form-control" id="license_file" name="license_file" accept=".txt,.csv" required>
<div class="form-text">支持TXT或CSV格式文件每行一个卡密</div>
</div>
<div class="mb-3">
<label for="license_type" class="form-label">卡密类型</label>
<select class="form-select" id="license_type" name="license_type">
<option value="">自动识别</option>
<option value="1">正式卡密</option>
<option value="0">试用卡密</option>
</select>
<div class="form-text">如果选择自动识别,系统会根据卡密前缀判断类型</div>
</div>
<div class="mb-3">
2025-11-19 22:49:24 +08:00
<label class="form-label">卡密时长</label>
<div class="row">
<div class="col-md-12">
<div class="btn-group" role="group" id="duration-type-group">
<input type="radio" class="btn-check" name="duration_type" id="duration-day" value="1" checked>
<label class="btn btn-outline-primary" for="duration-day">天卡(1天)</label>
<input type="radio" class="btn-check" name="duration_type" id="duration-month" value="30">
<label class="btn btn-outline-primary" for="duration-month">月卡(30天)</label>
<input type="radio" class="btn-check" name="duration_type" id="duration-quarter" value="90">
<label class="btn btn-outline-primary" for="duration-quarter">季卡(90天)</label>
<input type="radio" class="btn-check" name="duration_type" id="duration-year" value="365">
<label class="btn btn-outline-primary" for="duration-year">年卡(365天)</label>
<input type="radio" class="btn-check" name="duration_type" id="duration-permanent" value="-1">
<label class="btn btn-outline-primary" for="duration-permanent">永久卡</label>
<input type="radio" class="btn-check" name="duration_type" id="duration-custom" value="custom">
<label class="btn btn-outline-primary" for="duration-custom">自定义</label>
</div>
</div>
</div>
<div class="mt-2">
<input type="number" class="form-control" id="expire_days" name="expire_days" min="1" max="3650" value="1" style="display: none;">
<div class="form-text" id="duration-help-text">选择卡密的有效期类型</div>
</div>
2025-11-11 21:39:12 +08:00
</div>
<div class="mb-3">
<label for="max_devices" class="form-label">最大绑定设备数</label>
<input type="number" class="form-control" id="max_devices" name="max_devices" min="1" max="100" value="1">
<div class="form-text">同一卡密最多可绑定的设备数量</div>
</div>
<div class="mb-3">
<label for="description" class="form-label">备注</label>
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary" id="submit-btn">
<i class="fas fa-file-import me-2"></i>
<span id="submit-text">导入卡密</span>
</button>
<a href="{{ url_for('web.licenses') }}" 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>支持CSV格式第一列为卡密</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>
<h6 class="mt-3">示例文件格式</h6>
<pre class="bg-light p-2 small">LICENSE_KEY_001
LICENSE_KEY_002
TRIAL_KEY_001
TRIAL_KEY_002</pre>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initEventListeners();
2025-11-19 22:49:24 +08:00
// 初始化时长选择器
initDurationSelector();
2025-11-11 21:39:12 +08:00
});
// 初始化事件监听器
function initEventListeners() {
// 表单提交
document.getElementById('import-form').addEventListener('submit', function(e) {
e.preventDefault();
importLicenses();
});
}
2025-11-19 22:49:24 +08:00
// 初始化时长选择器
function initDurationSelector() {
// 获取所有时长类型单选按钮
const durationRadios = document.querySelectorAll('input[name="duration_type"]');
const expireDaysInput = document.getElementById('expire_days');
const durationHelpText = document.getElementById('duration-help-text');
// 为每个单选按钮添加事件监听器
durationRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'custom') {
// 显示自定义输入框
expireDaysInput.style.display = 'block';
expireDaysInput.focus();
durationHelpText.textContent = '请输入自定义有效期天数(1-3650天)';
} else {
// 隐藏自定义输入框
expireDaysInput.style.display = 'none';
durationHelpText.textContent = '选择卡密的有效期类型';
// 如果选择了永久卡,更新帮助文本
if (this.value === '-1') {
durationHelpText.textContent = '永久卡密永不过期';
}
}
});
});
}
2025-11-11 21:39:12 +08:00
// 导入卡密
function importLicenses() {
const submitBtn = document.getElementById('submit-btn');
const submitText = document.getElementById('submit-text');
const fileInput = document.getElementById('license_file');
// 获取表单数据
const formData = new FormData();
formData.append('product_id', document.getElementById('product_id').value);
formData.append('license_file', fileInput.files[0]);
const licenseType = document.getElementById('license_type').value;
if (licenseType) {
formData.append('license_type', licenseType);
}
2025-11-19 22:49:24 +08:00
// 获取选中的时长
const validDays = getSelectedDuration();
if (validDays !== null) {
formData.append('expire_days', validDays);
2025-11-11 21:39:12 +08:00
}
const maxDevices = document.getElementById('max_devices').value;
if (maxDevices) {
formData.append('max_devices', maxDevices);
}
const description = document.getElementById('description').value.trim();
if (description) {
formData.append('description', description);
}
// 基础验证
if (!document.getElementById('product_id').value) {
showNotification('请选择产品', 'warning');
return;
}
if (!fileInput.files[0]) {
showNotification('请选择要导入的文件', 'warning');
return;
}
// 显示加载状态
submitBtn.disabled = true;
submitText.textContent = '导入中...';
// 发送请求
showLoading();
2025-11-22 16:48:45 +08:00
apiRequest('/api/v1/licenses/import', {
2025-11-11 21:39:12 +08:00
method: 'POST',
2025-11-22 16:48:45 +08:00
body: formData
2025-11-11 21:39:12 +08:00
})
.then(response => {
hideLoading();
if (response.ok) {
return response.json();
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
})
.then(data => {
if (data.success) {
showNotification(`成功导入 ${data.data.imported} 个卡密,跳过 ${data.data.skipped} 个重复卡密`, 'success');
// 延迟跳转到卡密列表
setTimeout(() => {
window.location.href = '/licenses';
}, 2000);
} else {
showNotification(data.message || '导入失败', 'error');
}
})
.catch(error => {
hideLoading();
console.error('Failed to import licenses:', error);
showNotification('导入失败,请检查文件格式后重试', 'error');
})
.finally(() => {
// 恢复按钮状态
submitBtn.disabled = false;
submitText.textContent = '导入卡密';
});
}
2025-11-19 22:49:24 +08:00
// 获取选中的时长
function getSelectedDuration() {
const selectedRadio = document.querySelector('input[name="duration_type"]:checked');
if (!selectedRadio) return null;
if (selectedRadio.value === 'custom') {
const expireDaysInput = document.getElementById('expire_days');
const customValue = parseInt(expireDaysInput.value);
return isNaN(customValue) ? null : customValue;
}
return parseInt(selectedRadio.value);
}
2025-11-11 21:39:12 +08:00
</script>
{% endblock %}