Kamixitong/app/web/templates/user/product_detail.html
2025-12-12 11:35:14 +08:00

508 lines
22 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 "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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
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 %}