初始化鸿蒙应用展示平台项目 - 前后端分离架构

This commit is contained in:
Nvex
2025-10-25 11:45:17 +08:00
commit c0f81dbbe2
92 changed files with 40210 additions and 0 deletions

475
templates/coming.html Executable file
View File

@@ -0,0 +1,475 @@
{% extends "base.html" %}
{% block content %}
<div class="coming-apps-page">
<div id="toast" class="developing-tip">
<span id="toastMessage"></span>
</div>
<div class="header">
<a href="{{ url_for('explore') }}" class="back-link" title="返回探索">
<i class="fas fa-arrow-left"></i>
</a>
<h1>即将上线</h1>
</div>
<div class="apps-grid">
{% if coming_apps %}
{% for app in coming_apps %}
<div class="app-tile">
<div class="app-tile-content">
<div class="app-tile-icon">
{% if 'http' in app.icon_path %}
<img src="{{ app.icon_path }}" alt="{{ app.name }}">
{% else %}
<img src="{{ url_for('static', filename='uploads/' + app.icon_path) }}" alt="{{ app.name }}">
{% endif %}
</div>
<div class="app-tile-info">
<div class="app-tile-header">
<h3>{{ app.name }}</h3>
<button class="add-reminder-btn" onclick="toggleWishlist('{{ app.name }}')" title="添加提醒" data-app-name="{{ app.name }}">
<i class="fas fa-bell"></i>
</button>
</div>
<div class="app-tile-meta">
<span class="coming-soon-tag">即将上线</span>
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="no-apps-message">暂无即将上线应用</div>
{% endif %}
</div>
</div>
<style>
.coming-apps-page {
max-width: 1200px;
margin: 0 auto;
padding: 60px 15px 20px 15px;
transform: none !important;
transition: none !important;
}
.header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 30px;
background: white;
padding: 10px 15px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: none !important;
transition: none !important;
}
.back-link {
color: #666;
text-decoration: none;
padding: 8px;
border-radius: 50%;
background: #f5f5f7;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
transform: none !important;
transition: none !important;
}
.back-link:hover {
background: #e5e5e7;
color: #333;
}
.header h1 {
margin: 0;
font-size: 20px;
color: #333;
font-weight: 500;
font-family: "SimHei", "黑体", sans-serif;
transform: none !important;
transition: none !important;
}
.apps-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 15px;
}
.app-tile {
background: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.app-tile-content {
padding: 15px;
display: flex;
gap: 12px;
position: relative;
}
.app-tile-icon {
width: 64px;
height: 64px;
flex-shrink: 0;
}
.app-tile-icon img {
width: 100%;
height: 100%;
border-radius: 16px;
object-fit: cover;
}
.app-tile-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.app-tile-header {
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.app-tile-header h3 {
margin: 0 0 6px 0;
font-size: 16px;
color: #333;
font-weight: 500;
}
.coming-soon-tag {
display: inline-block;
font-size: 12px;
color: #0066cc;
background: rgba(0, 102, 204, 0.1);
padding: 2px 8px;
border-radius: 12px;
}
.no-apps-message {
grid-column: 1 / -1;
text-align: center;
padding: 40px;
color: #666;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.app-tile-meta {
display: flex;
align-items: center;
gap: 10px;
}
.add-reminder-btn {
border: none;
background: rgba(0, 102, 204, 0.1);
color: #0066cc;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
margin-left: 8px;
flex-shrink: 0;
}
.add-reminder-btn:hover {
background: rgba(0, 102, 204, 0.2);
color: #0066cc;
transform: scale(1.05);
}
.add-reminder-btn.active {
background: #0066cc;
color: white;
}
.add-reminder-btn.active:hover {
background: #0052a3;
color: white;
}
.add-reminder-btn i {
font-size: 14px;
}
.developing-tip {
position: fixed;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 25px;
font-size: 14px;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: none;
transform: translate(-50%, 20px);
}
[data-theme="dark"] .developing-tip {
background: rgba(255, 255, 255, 0.9);
color: black;
}
.developing-tip.show {
opacity: 1;
transform: translate(-50%, 0);
}
@keyframes tipAppear {
0% {
opacity: 0;
transform: translate(-50%, 20px);
}
20% {
opacity: 1;
transform: translate(-50%, 0);
}
80% {
opacity: 1;
transform: translate(-50%, 0);
}
100% {
opacity: 0;
transform: translate(-50%, -20px);
}
}
.developing-tip.show {
animation: tipAppear 2s ease forwards;
}
/* 暗色模式样式 */
[data-theme="dark"] .add-reminder-btn {
background: rgba(94, 158, 255, 0.1);
color: #5E9EFF;
}
[data-theme="dark"] .add-reminder-btn:hover {
background: rgba(94, 158, 255, 0.2);
color: #5E9EFF;
}
[data-theme="dark"] .add-reminder-btn.active {
background: #5E9EFF;
color: white;
}
[data-theme="dark"] .add-reminder-btn.active:hover {
background: #4b8fef;
color: white;
}
[data-theme="dark"] .coming-apps-page {
background: #1a1a1a;
}
[data-theme="dark"] .header {
background: #242424;
}
[data-theme="dark"] .header h1 {
color: #fff;
}
[data-theme="dark"] .back-link {
color: #ccc;
background: #333;
}
[data-theme="dark"] .back-link:hover {
background: #444;
color: #fff;
}
[data-theme="dark"] .app-tile {
background: #242424;
}
[data-theme="dark"] .app-tile-header h3 {
color: #fff;
}
[data-theme="dark"] .no-apps-message {
background: #242424;
color: #ccc;
}
[data-theme="dark"] .coming-soon-tag {
background: rgba(94, 158, 255, 0.1);
color: #5E9EFF;
}
@media (max-width: 768px) {
.apps-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
// 存储心愿单应用列表
let wishlistApps = new Set();
// 加载心愿单
function loadWishlist() {
fetch('/user/wishlist/list')
.then(response => response.json())
.then(data => {
if (data.success) {
// 清空并重新填充心愿单集合
wishlistApps.clear();
data.items.forEach(item => wishlistApps.add(item.app_name));
// 更新所有按钮状态
updateAllButtonStates();
}
})
.catch(error => {
console.error('Load wishlist failed:', error);
});
}
// 更新所有按钮状态
function updateAllButtonStates() {
document.querySelectorAll('.add-reminder-btn').forEach(btn => {
const appName = btn.dataset.appName;
updateButtonState(btn, wishlistApps.has(appName));
});
}
// 更新单个按钮状态
function updateButtonState(button, isActive) {
const icon = button.querySelector('i');
if (isActive) {
button.classList.add('active');
button.title = '取消提醒';
icon.className = 'fas fa-check-circle';
} else {
button.classList.remove('active');
button.title = '添加提醒';
icon.className = 'fas fa-bell';
}
}
// 切换心愿单状态
function toggleWishlist(appName) {
const button = document.querySelector(`.add-reminder-btn[data-app-name="${appName}"]`);
const isRemoving = wishlistApps.has(appName);
if (isRemoving) {
// 获取项目ID并删除
fetch('/user/wishlist/list')
.then(response => response.json())
.then(data => {
if (data.success) {
const item = data.items.find(item => item.app_name === appName);
if (item) {
deleteFromWishlist(item.id);
}
}
});
} else {
addToWishlist(appName);
}
}
// 添加到心愿单
function addToWishlist(appName) {
fetch('/user/wishlist/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
app_name: appName
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
wishlistApps.add(appName);
updateAllButtonStates();
showToast('添加成功');
} else {
if (data.error.includes('邀请3位好友')) {
showToast('当前最多添加5个应用邀请3位好友可提升至20个');
} else if (data.error.includes('已达到心愿单上限')) {
showToast('已达到心愿单上限20个');
} else {
showToast(data.error || '添加失败');
}
}
})
.catch(error => {
console.error('Add to wishlist failed:', error);
showToast('添加失败,请稍后重试');
});
}
// 从心愿单中删除
function deleteFromWishlist(itemId) {
fetch(`/user/wishlist/delete/${itemId}`, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadWishlist(); // 重新加载心愿单以更新状态
showToast('已取消提醒');
} else {
showToast(data.error || '取消失败');
}
})
.catch(error => {
console.error('Delete from wishlist failed:', error);
showToast('取消失败,请稍后重试');
});
}
// Toast 提示函数
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 2000);
}
// 返回按钮处理
document.querySelector('.back-link').addEventListener('click', function(e) {
e.preventDefault();
window.history.back();
});
// 页面加载时获取心愿单
document.addEventListener('DOMContentLoaded', loadWishlist);
</script>
{% endblock %}