508 lines
22 KiB
HTML
508 lines
22 KiB
HTML
{% extends "user/base.html" %}
|
||
|
||
{% block title %}产品详情 - {{ config.SITE_NAME or '软件授权管理系统' }}{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.product-detail-image {
|
||
max-height: 400px;
|
||
object-fit: cover;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.version-table th {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.download-section {
|
||
background: #f8f9fa;
|
||
border-radius: 10px;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.feature-list li {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container py-4">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<nav aria-label="breadcrumb">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item"><a href="{{ url_for('user.user_index') }}">首页</a></li>
|
||
<li class="breadcrumb-item"><a href="{{ url_for('user.user_products') }}">产品中心</a></li>
|
||
<li class="breadcrumb-item active" aria-current="page">产品详情</li>
|
||
</ol>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="card shadow-sm">
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-lg-6 mb-4 mb-lg-0">
|
||
<img src="/static/images/product-default.png"
|
||
class="img-fluid product-detail-image"
|
||
alt="产品图片" id="productImage">
|
||
</div>
|
||
<div class="col-lg-6">
|
||
<h1 class="mb-3" id="productName">产品名称</h1>
|
||
<p class="lead" id="productDescription">产品描述信息,这里是产品的详细介绍内容。</p>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-sm-4"><strong>产品版本:</strong></div>
|
||
<div class="col-sm-8" id="productVersion">v1.0.0</div>
|
||
</div>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-sm-4"><strong>发布状态:</strong></div>
|
||
<div class="col-sm-8">
|
||
<span class="badge bg-success" id="productStatus">已发布</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-sm-4"><strong>分类:</strong></div>
|
||
<div class="col-sm-8" id="productCategory">办公软件</div>
|
||
</div>
|
||
|
||
<div class="row mb-4">
|
||
<div class="col-sm-4"><strong>更新时间:</strong></div>
|
||
<div class="col-sm-8" id="updateTime">2023-10-01 10:30:00</div>
|
||
</div>
|
||
|
||
<div class="download-section">
|
||
<h4 class="mb-3">
|
||
<i class="fas fa-download me-2"></i>下载产品
|
||
</h4>
|
||
|
||
<div class="input-group mb-3">
|
||
<input type="text" class="form-control"
|
||
id="licenseKey"
|
||
placeholder="请输入卡密">
|
||
<button class="btn btn-outline-secondary"
|
||
type="button"
|
||
id="verifyLicenseBtn">
|
||
验证
|
||
</button>
|
||
</div>
|
||
|
||
<div id="downloadSection" style="display: none;">
|
||
<div class="alert alert-success">
|
||
<i class="fas fa-check-circle me-2"></i>
|
||
卡密验证成功!
|
||
</div>
|
||
<button class="btn btn-success w-100" id="downloadBtn">
|
||
<i class="fas fa-download me-2"></i>
|
||
立即下载
|
||
</button>
|
||
</div>
|
||
|
||
<div class="mt-3 text-center">
|
||
<a href="{{ url_for('user.user_license_purchase') }}" class="btn btn-link">
|
||
<i class="fas fa-shopping-cart me-1"></i>购买卡密
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-4" id="featuresSection" style="display: none;">
|
||
<div class="col-12">
|
||
<div class="card shadow-sm">
|
||
<div class="card-header">
|
||
<h5 class="mb-0">产品功能特性</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<ul class="feature-list" id="featuresList">
|
||
<!-- 功能特性将通过JavaScript动态加载 -->
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mt-4">
|
||
<div class="col-12">
|
||
<div class="card shadow-sm">
|
||
<div class="card-header">
|
||
<h5 class="mb-0">版本历史</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="table-responsive">
|
||
<table class="table table-striped version-table">
|
||
<thead>
|
||
<tr>
|
||
<th>版本</th>
|
||
<th>发布日期</th>
|
||
<th>更新内容</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="versionHistory">
|
||
<tr>
|
||
<td>v1.0.0</td>
|
||
<td>2023-10-01</td>
|
||
<td>初始版本发布</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-outline-primary">
|
||
<i class="fas fa-download me-1"></i>下载
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script>
|
||
// 产品详情页面JavaScript
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
console.log('产品详情页面加载完成');
|
||
|
||
// 从URL中获取产品ID
|
||
const urlPath = window.location.pathname;
|
||
const productId = urlPath.split('/').pop();
|
||
|
||
// 加载产品详情
|
||
loadProductDetail(productId);
|
||
|
||
// 验证卡密按钮事件
|
||
document.getElementById('verifyLicenseBtn').addEventListener('click', function() {
|
||
const licenseKey = document.getElementById('licenseKey').value.trim();
|
||
|
||
if (!licenseKey) {
|
||
showNotification('请输入卡密', 'warning');
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
showLoading();
|
||
|
||
// 调用API验证卡密
|
||
apiRequest('/user/licenses/verify', {
|
||
method: 'POST',
|
||
body: JSON.stringify({ license_key: licenseKey })
|
||
})
|
||
.then(data => {
|
||
if (data.success) {
|
||
// 验证成功,显示下载按钮
|
||
document.getElementById('downloadSection').style.display = 'block';
|
||
showNotification('卡密验证成功!', 'success');
|
||
} else {
|
||
showNotification('卡密验证失败: ' + data.message, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showNotification('验证卡密失败,请稍后重试', 'error');
|
||
console.error('验证卡密失败:', error);
|
||
})
|
||
.finally(() => {
|
||
hideLoading();
|
||
});
|
||
});
|
||
|
||
// 下载按钮事件
|
||
document.getElementById('downloadBtn').addEventListener('click', function() {
|
||
showNotification('开始下载...', 'info');
|
||
// 这里应该触发实际的下载逻辑
|
||
});
|
||
});
|
||
|
||
// 加载产品详情
|
||
function loadProductDetail(productId) {
|
||
if (!productId) {
|
||
showNotification('产品ID无效', 'error');
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
showLoading();
|
||
|
||
// 调用API获取产品详情
|
||
apiRequest(`/api/v1/user/products/${productId}`)
|
||
.then(data => {
|
||
if (data.success) {
|
||
renderProductDetail(data.data);
|
||
} else {
|
||
showNotification('加载产品详情失败: ' + data.message, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showNotification('加载产品详情失败,请稍后重试', 'error');
|
||
console.error('加载产品详情失败:', error);
|
||
})
|
||
.finally(() => {
|
||
hideLoading();
|
||
});
|
||
}
|
||
|
||
// 转义HTML特殊字符
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
const map = {
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>',
|
||
'"': '"',
|
||
"'": '''
|
||
};
|
||
return text.replace(/[&<>'"]/g, function(m) { return map[m]; });
|
||
}
|
||
|
||
// 渲染产品详情
|
||
function renderProductDetail(product) {
|
||
// 更新页面内容
|
||
document.getElementById('productName').textContent = product.product_name;
|
||
document.getElementById('productDescription').textContent = product.description || '暂无描述';
|
||
document.getElementById('productVersion').textContent = product.latest_version || '1.0.0';
|
||
document.getElementById('productStatus').textContent = product.status_name;
|
||
document.getElementById('productStatus').className = `badge ${product.status === 1 ? 'bg-success' : 'bg-secondary'}`;
|
||
document.getElementById('updateTime').textContent = product.update_time;
|
||
|
||
// 更新产品图片
|
||
const productImage = document.getElementById('productImage');
|
||
if (product.image_url) {
|
||
productImage.src = product.image_url;
|
||
} else {
|
||
productImage.src = '/static/images/product-default.png';
|
||
}
|
||
|
||
// 更新功能特性
|
||
const featuresList = document.getElementById('featuresList');
|
||
const featuresSection = document.getElementById('featuresSection');
|
||
if (product.features) {
|
||
// 将功能特性按行分割并渲染
|
||
const features = product.features.split('\n').filter(f => f.trim() !== '');
|
||
if (features.length > 0) {
|
||
let featuresHtml = '';
|
||
features.forEach(feature => {
|
||
featuresHtml += `<li><i class="fas fa-check-circle text-success me-2"></i>${escapeHtml(feature)}</li>`;
|
||
});
|
||
featuresList.innerHTML = featuresHtml;
|
||
featuresSection.style.display = 'block';
|
||
} else {
|
||
featuresSection.style.display = 'none';
|
||
}
|
||
} else {
|
||
featuresSection.style.display = 'none';
|
||
}
|
||
|
||
// 更新版本历史
|
||
const versionHistory = document.getElementById('versionHistory');
|
||
if (product.recent_updates && product.recent_updates.length > 0) {
|
||
let versionHtml = '';
|
||
product.recent_updates.forEach(update => {
|
||
// 安全地处理下载URL
|
||
const downloadUrl = update.download_url ? escapeHtml(update.download_url) : '';
|
||
console.log('Rendering version update:', update);
|
||
versionHtml += `
|
||
<tr>
|
||
<td>${escapeHtml(update.version_num)}</td>
|
||
<td>${formatDate(update.update_time)}</td>
|
||
<td>${escapeHtml(update.update_log || '无更新说明')}</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-outline-primary download-version-btn"
|
||
data-product="${escapeHtml(product.product_id)}"
|
||
data-version="${escapeHtml(update.version_num)}"
|
||
data-url="${downloadUrl}">
|
||
<i class="fas fa-download me-1"></i>下载
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
});
|
||
versionHistory.innerHTML = versionHtml;
|
||
|
||
// 为每个下载按钮添加点击事件
|
||
document.querySelectorAll('.download-version-btn').forEach(button => {
|
||
button.addEventListener('click', function() {
|
||
const productId = this.getAttribute('data-product');
|
||
const versionNum = this.getAttribute('data-version');
|
||
const downloadUrl = this.getAttribute('data-url');
|
||
console.log('Download button clicked:', {productId, versionNum, downloadUrl});
|
||
downloadVersion(productId, versionNum, downloadUrl);
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// 添加下载版本的函数
|
||
function downloadVersion(productId, versionNum, downloadUrl) {
|
||
console.log('Download version called:', {productId, versionNum, downloadUrl});
|
||
showLoading();
|
||
|
||
// 如果有直接的下载URL,直接处理
|
||
if (downloadUrl && downloadUrl.trim() !== '') {
|
||
try {
|
||
console.log('Processing direct download URL:', downloadUrl);
|
||
// 检查URL是否是本地上传的文件(相对路径)
|
||
if (downloadUrl.startsWith('/')) {
|
||
// 本地文件,直接下载
|
||
window.open(downloadUrl, '_blank');
|
||
showNotification('开始下载...', 'info');
|
||
} else {
|
||
// 外部链接,询问用户是否跳转
|
||
if (confirm(`此版本是外部链接,将跳转到:${downloadUrl}\n是否继续?`)) {
|
||
window.open(downloadUrl, '_blank');
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('下载处理出错:', e);
|
||
showNotification('下载处理出错,请稍后重试', 'error');
|
||
}
|
||
hideLoading();
|
||
return;
|
||
}
|
||
|
||
// 如果没有直接的下载URL,检查下载权限
|
||
console.log('Checking download permissions for product:', productId);
|
||
apiRequest(`/api/v1/user/downloads/check?product_id=${productId}`)
|
||
.then(data => {
|
||
console.log('Download permission check result:', data);
|
||
if (data.success) {
|
||
const downloadInfo = data.data;
|
||
console.log('Download info:', downloadInfo);
|
||
|
||
// 如果有下载链接,直接处理
|
||
if (downloadInfo.download_url && downloadInfo.download_url.trim() !== '') {
|
||
try {
|
||
console.log('Processing download URL from API:', downloadInfo.download_url);
|
||
// 检查URL是否是本地上传的文件(相对路径)
|
||
if (downloadInfo.download_url.startsWith('/')) {
|
||
// 本地文件,直接下载
|
||
window.open(downloadInfo.download_url, '_blank');
|
||
showNotification('开始下载...', 'info');
|
||
} else {
|
||
// 外部链接,询问用户是否跳转
|
||
if (confirm(`此版本是外部链接,将跳转到:${downloadInfo.download_url}\n是否继续?`)) {
|
||
window.open(downloadInfo.download_url, '_blank');
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('下载处理出错:', e);
|
||
showNotification('下载处理出错,请稍后重试', 'error');
|
||
}
|
||
} else {
|
||
console.log('No download URL available in API response');
|
||
// 检查是否是付费产品
|
||
if (downloadInfo.is_paid) {
|
||
// 显示卡密输入模态框
|
||
showLicenseVerificationModal(productId, versionNum, downloadInfo.download_url);
|
||
} else {
|
||
showNotification('暂无下载链接', 'warning');
|
||
}
|
||
}
|
||
} else {
|
||
showNotification('检查下载权限失败: ' + data.message, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('检查下载权限失败:', error);
|
||
showNotification('检查下载权限失败,请稍后重试', 'error');
|
||
})
|
||
.finally(() => {
|
||
hideLoading();
|
||
});
|
||
}
|
||
|
||
// 显示卡密验证模态框
|
||
function showLicenseVerificationModal(productId, versionNum, downloadUrl) {
|
||
// 创建模态框HTML
|
||
const modalHtml = `
|
||
<div class="modal fade" id="licenseVerifyModal" tabindex="-1" aria-labelledby="licenseVerifyModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="licenseVerifyModalLabel">卡密验证</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-3">
|
||
<label for="licenseKeyInput" class="form-label">请输入您的卡密</label>
|
||
<input type="text" class="form-control" id="licenseKeyInput" placeholder="请输入卡密">
|
||
<div class="form-text">版本: ${versionNum}</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||
<button type="button" class="btn btn-primary" id="verifyLicenseBtn">验证并下载</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 添加到页面中
|
||
if (!document.getElementById('licenseVerifyModal')) {
|
||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||
}
|
||
|
||
// 显示模态框
|
||
const modal = new bootstrap.Modal(document.getElementById('licenseVerifyModal'));
|
||
modal.show();
|
||
|
||
// 绑定验证按钮事件
|
||
document.getElementById('verifyLicenseBtn').onclick = function() {
|
||
const licenseKey = document.getElementById('licenseKeyInput').value.trim();
|
||
if (!licenseKey) {
|
||
showNotification('请输入卡密', 'warning');
|
||
return;
|
||
}
|
||
|
||
verifyAndDownload(productId, licenseKey, downloadUrl);
|
||
};
|
||
}
|
||
|
||
// 验证卡密并下载
|
||
function verifyAndDownload(productId, licenseKey, downloadUrl) {
|
||
showLoading();
|
||
|
||
apiRequest(`/api/v1/user/licenses/verify?license_key=${encodeURIComponent(licenseKey)}&product_id=${productId}`)
|
||
.then(data => {
|
||
if (data.success) {
|
||
// 验证成功,开始下载
|
||
if (data.data.download_url) {
|
||
window.open(data.data.download_url, '_blank');
|
||
showNotification('验证成功,开始下载...', 'success');
|
||
|
||
// 关闭模态框
|
||
const modalElement = document.getElementById('licenseVerifyModal');
|
||
if (modalElement) {
|
||
const modal = bootstrap.Modal.getInstance(modalElement);
|
||
if (modal) {
|
||
modal.hide();
|
||
}
|
||
}
|
||
} else {
|
||
showNotification('暂无下载链接', 'warning');
|
||
}
|
||
} else {
|
||
showNotification('卡密验证失败: ' + data.message, 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
showNotification('卡密验证失败,请稍后重试', 'error');
|
||
console.error('卡密验证失败:', error);
|
||
})
|
||
.finally(() => {
|
||
hideLoading();
|
||
});
|
||
}
|
||
</script>
|
||
{% endblock %} |