Kamixitong/app/web/templates/license/import.html
2025-11-22 16:48:45 +08:00

279 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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">
<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>
</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();
// 初始化时长选择器
initDurationSelector();
});
// 初始化事件监听器
function initEventListeners() {
// 表单提交
document.getElementById('import-form').addEventListener('submit', function(e) {
e.preventDefault();
importLicenses();
});
}
// 初始化时长选择器
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 = '永久卡密永不过期';
}
}
});
});
}
// 导入卡密
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);
}
// 获取选中的时长
const validDays = getSelectedDuration();
if (validDays !== null) {
formData.append('expire_days', validDays);
}
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();
apiRequest('/api/v1/licenses/import', {
method: 'POST',
body: formData
})
.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 = '导入卡密';
});
}
// 获取选中的时长
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);
}
</script>
{% endblock %}