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

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

784
templates/new_apps.html Executable file
View File

@@ -0,0 +1,784 @@
{% extends "base.html" %}
{% block content %}
<!--<div id="wishlist-announcement" class="wishlist-announcement">-->
<!-- <div class="announcement-content">-->
<!-- <span class="announcement-text">🎉 心愿单功能全新上线</span>-->
<!-- <button onclick="window.location.href='{{ url_for('more') }}'" class="goto-btn">立即体验</button>-->
<!-- <button onclick="closeAnnouncement()" class="close-btn">×</button>-->
<!-- </div>-->
<!-- <div class="countdown-bar"></div>-->
<!--</div>-->
<!-- 添加水印容器 -->
<div class="watermark-container">
<div class="watermark-content"></div>
</div>
<div class="new-apps-page">
<div class="header">
<a href="{{ url_for('explore') }}" class="back-link" title="返回首页" onclick="handleBackClick(event)">
<i class="fas fa-arrow-left"></i>
</a>
<h1>{{ date_text }}</h1>
</div>
<div class="date-switcher">
<a href="{{ url_for('new_apps', date='today') }}" class="date-btn {{ 'active' if date == 'today' }}">今日</a>
<a href="{{ url_for('new_apps', date='yesterday') }}" class="date-btn {{ 'active' if date == 'yesterday' }}">昨日</a>
<a href="{{ url_for('new_apps', date='before_yesterday') }}" class="date-btn {{ 'active' if date == 'before_yesterday' }}">前日</a>
</div>
{% if today_apps %}
<div class="apps-grid">
{% for app in today_apps %}
<div class="app-tile" onclick="handleAppClick(event, '{{ app.id }}')">
<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>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<i class="fas fa-inbox"></i>
<p>今日无上新应用</p>
</div>
{% endif %}
</div>
<style>
.goto-btn {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 6px 14px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
.goto-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
border-color: rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 20px;
cursor: pointer;
padding: 0 6px;
opacity: 0.8;
transition: opacity 0.3s ease;
}
.close-btn:hover {
opacity: 1;
}
.countdown-bar {
height: 3px;
background: rgba(255, 255, 255, 0.2);
border-radius: 0;
overflow: hidden;
position: relative;
margin: 0 -12px;
width: calc(100% + 24px);
}
.countdown-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: linear-gradient(90deg, rgba(255,255,255,0.8), rgba(255,255,255,1));
animation: countdown 5s linear forwards;
}
@keyframes slideDown {
from {
transform: translate(-50%, -100%);
opacity: 0;
}
to {
transform: translate(-50%, 0);
opacity: 1;
}
}
@keyframes countdown {
from {
width: 100%;
}
to {
width: 0;
}
}
/* 暗色模式适配 */
[data-theme="dark"] .wishlist-announcement {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* 心愿单通知弹窗样式 */
.wishlist-announcement {
position: fixed;
top: 60px;
left: 50%;
transform: translateX(-50%);
width: 90%;
max-width: 400px;
background: linear-gradient(135deg, #4F46E5, #7C3AED);
border-radius: 12px;
padding: 12px 12px 0 12px;
box-shadow: 0 8px 20px rgba(124, 58, 237, 0.25);
z-index: 1000;
overflow: hidden;
animation: slideDown 0.5s ease-out;
touch-action: pan-x; /* 允许水平滑动 */
user-select: none; /* 防止文本选择 */
transition: transform 0.3s ease; /* 添加平滑过渡 */
}
/* 添加滑动时的动画效果 */
.wishlist-announcement.swiping {
transition: transform 0.1s ease;
}
/* 添加滑动完成后的动画效果 */
.wishlist-announcement.swipe-out {
transform: translate(calc(-50% - 150%), 0);
opacity: 0;
}
.announcement-content {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.announcement-text {
color: white;
font-size: 16px;
font-weight: 600;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* 添加水印相关样式 */
.watermark-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 1000;
overflow: hidden;
}
.watermark-content {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
background-repeat: repeat;
opacity: 0.2;
transform: rotate(-15deg);
pointer-events: none;
will-change: transform;
backface-visibility: hidden;
-webkit-font-smoothing: antialiased;
}
/* 保持原有样式不变 */
.new-apps-page {
max-width: 1200px;
margin: 60px auto 20px;
padding: 0 15px;
position: relative;
z-index: 1;
}
.header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 15px;
background: white;
padding: 5px 20px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: none !important;
transition: none !important;
animation: none !important;
}
.header * {
transform: none !important;
transition: none !important;
animation: none !important;
}
.back-link {
color: #666;
text-decoration: none;
padding: 8px;
border-radius: 50%;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}
.header h1 {
margin: 0;
font-size: 20px;
color: #333;
font-weight: 500;
font-family: "SimHei", "黑体", sans-serif;
text-align: left;
}
/* 添加空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-top: 20px;
}
.empty-state i {
font-size: 48px;
color: #ccc;
margin-bottom: 16px;
}
.empty-state p {
font-size: 16px;
color: #666;
margin: 0;
font-family: "SimHei", "黑体", sans-serif;
}
/* 添加网格布局样式 */
.apps-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 12px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin: 0;
padding: 0;
}
/* 修改应用图标容器样式 */
.app-tile {
cursor: pointer;
transition: transform 0.2s;
text-align: center;
padding: 8px;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
}
.app-tile:hover {
transform: translateY(-2px);
background: #f5f5f5;
}
/* 修改图标容器样式 */
.app-tile-icon {
width: 60px;
height: 60px;
margin: 0 auto 6px;
}
.app-tile-icon img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 14px;
}
/* 修改应用名称样式 */
.app-tile-header h3 {
margin: 0;
font-size: 12px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.3;
max-height: 2.6em;
width: 100%;
text-align: center;
}
/* 修改响应式布局 */
@media (max-width: 768px) {
.apps-grid {
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
gap: 10px;
}
.app-tile-icon {
width: 52px;
height: 52px;
}
.app-tile-header h3 {
font-size: 11px;
}
}
@media (max-width: 480px) {
.apps-grid {
grid-template-columns: repeat(4, 1fr); /* 强制一行4个 */
gap: 8px;
}
.app-tile {
padding: 6px;
}
.app-tile-icon {
width: 48px;
height: 48px;
}
.app-tile-header h3 {
font-size: 11px;
}
}
/* 暗色模式样式 */
[data-theme="dark"] .new-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"] .apps-grid {
background: #242424;
}
[data-theme="dark"] .app-tile {
background: #242424;
}
[data-theme="dark"] .app-tile:hover {
background: #333;
}
[data-theme="dark"] .app-tile-header h3 {
color: #fff;
}
[data-theme="dark"] .empty-state {
background: #242424;
}
[data-theme="dark"] .empty-state i {
color: #666;
}
[data-theme="dark"] .empty-state p {
color: #ccc;
}
/* 修改水印在暗色模式下的样式 */
[data-theme="dark"] .watermark-content {
opacity: 0.1;
}
/* 修改水印文字在暗色模式下的颜色 */
[data-theme="dark"] .watermark-content canvas {
filter: invert(1); /* 反转颜色 */
}
/* 日期切换器样式 */
.date-switcher {
display: flex;
gap: 8px;
margin-bottom: 8px;
background: white;
padding: 10px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
justify-content: center;
}
.date-btn {
padding: 6px 16px;
border-radius: 6px;
background: #f0f0f0;
color: #666;
text-decoration: none;
font-size: 14px;
transition: all 0.2s;
}
.date-btn:hover {
background: #e0e0e0;
}
.date-btn.active {
background: #007aff;
color: white;
}
/* 暗色模式样式 */
[data-theme="dark"] .date-switcher {
background: #242424;
}
[data-theme="dark"] .date-btn {
background: #333;
color: #ccc;
}
[data-theme="dark"] .date-btn:hover {
background: #444;
}
[data-theme="dark"] .date-btn.active {
background: #0056b3;
color: white;
}
/* 响应式布局调整 */
@media (max-width: 480px) {
.header {
padding: 10px;
}
.date-switcher {
padding: 8px;
}
.date-btn {
padding: 4px 12px;
font-size: 12px;
}
}
/* 修复暗色模式切换按钮在 PC 端的显示问题 */
.floating-buttons {
position: fixed;
bottom: 40px;
right: 20px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 10px;
}
.theme-toggle {
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
padding: 0;
}
.theme-toggle:hover {
transform: scale(1.1);
}
/* 暗色模式样式 */
[data-theme="dark"] .theme-toggle {
background: #333;
color: #fff;
}
/* 响应式调整 */
@media (max-width: 768px) {
.floating-buttons {
bottom: 30px;
right: 15px;
gap: 8px;
}
.theme-toggle {
width: 36px;
height: 36px;
}
}
/* 添加关闭动画 */
@keyframes slideUp {
from {
transform: translate(-50%, 0);
opacity: 1;
}
to {
transform: translate(-50%, -100%);
opacity: 0;
}
}
</style>
<script>
function handleAppClick(event, appId) {
window.location.href = '/app/' + appId;
}
function handleBackClick(event) {
event.preventDefault();
// 检查是否是从explore页面来的
if (sessionStorage.getItem('fromExplore')) {
window.location.href = '/explore';
} else {
window.location.href = '/';
}
}
// 心愿单通知弹窗相关函数
function closeAnnouncement() {
const announcement = document.getElementById('wishlist-announcement');
if (!announcement) return;
announcement.style.animation = 'slideUp 0.5s ease-out forwards';
setTimeout(() => {
announcement.remove();
}, 500);
}
// 添加自动关闭功能
document.addEventListener('DOMContentLoaded', () => {
setTimeout(closeAnnouncement, 5000);
});
let touchStartX = 0;
let touchEndX = 0;
let currentTranslateX = 0;
let isDragging = false;
document.addEventListener('DOMContentLoaded', () => {
const announcement = document.getElementById('wishlist-announcement');
if (!announcement) return;
// 触摸开始
announcement.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
isDragging = true;
announcement.classList.add('swiping');
});
// 触摸移动
announcement.addEventListener('touchmove', (e) => {
if (!isDragging) return;
touchEndX = e.touches[0].clientX;
const diffX = touchEndX - touchStartX;
// 只允许左滑
if (diffX > 0) {
currentTranslateX = 0;
} else {
currentTranslateX = diffX;
}
// 应用变换
announcement.style.transform = `translate(calc(-50% + ${currentTranslateX}px), 0)`;
});
// 触摸结束
announcement.addEventListener('touchend', () => {
isDragging = false;
announcement.classList.remove('swiping');
// 如果滑动距离超过阈值,则关闭通知
if (currentTranslateX < -100) {
announcement.classList.add('swipe-out');
setTimeout(() => {
announcement.remove();
}, 300);
} else {
// 否则回弹
announcement.style.transform = 'translateX(-50%)';
}
// 重置状态
currentTranslateX = 0;
touchStartX = 0;
touchEndX = 0;
});
// 鼠标事件支持
announcement.addEventListener('mousedown', (e) => {
touchStartX = e.clientX;
isDragging = true;
announcement.classList.add('swiping');
});
announcement.addEventListener('mousemove', (e) => {
if (!isDragging) return;
touchEndX = e.clientX;
const diffX = touchEndX - touchStartX;
if (diffX > 0) {
currentTranslateX = 0;
} else {
currentTranslateX = diffX;
}
announcement.style.transform = `translate(calc(-50% + ${currentTranslateX}px), 0)`;
});
announcement.addEventListener('mouseup', () => {
if (!isDragging) return;
isDragging = false;
announcement.classList.remove('swiping');
if (currentTranslateX < -100) {
announcement.classList.add('swipe-out');
setTimeout(() => {
announcement.remove();
}, 300);
} else {
announcement.style.transform = 'translateX(-50%)';
}
currentTranslateX = 0;
touchStartX = 0;
touchEndX = 0;
});
// 处理鼠标离开窗口的情况
announcement.addEventListener('mouseleave', () => {
if (isDragging) {
isDragging = false;
announcement.classList.remove('swiping');
announcement.style.transform = 'translateX(-50%)';
currentTranslateX = 0;
touchStartX = 0;
touchEndX = 0;
}
});
});
// 监听浏览器返回按钮
window.addEventListener('popstate', function(event) {
if (sessionStorage.getItem('fromExplore')) {
window.location.href = '/explore';
}
});
// 修改水印相关脚本
document.addEventListener('DOMContentLoaded', function() {
const watermarkContent = document.querySelector('.watermark-content');
const settings = {{ settings|tojson|safe }};
function createWatermark() {
const text1 = settings.watermark_text_1 || '';
const text2 = settings.watermark_text_2 || '';
if (!text1 && !text2) return;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 160;
ctx.font = '16px Arial';
// 根据主题设置颜色
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
ctx.fillStyle = isDark ? '#ffffff' : '#333333';
ctx.textAlign = 'center';
ctx.lineWidth = 0.6;
if (text1) {
ctx.fillText(text1, canvas.width/2, canvas.height/2 - 8);
ctx.strokeText(text1, canvas.width/2, canvas.height/2 - 8);
}
if (text2) {
ctx.fillText(text2, canvas.width/2, canvas.height/2 + 8);
ctx.strokeText(text2, canvas.width/2, canvas.height/2 + 8);
}
const pattern = ctx.createPattern(canvas, 'repeat');
watermarkContent.style.backgroundImage = `url(${canvas.toDataURL()})`;
}
createWatermark();
// 监听主题变化,重新创建水印
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
createWatermark();
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
});
</script>
{% endblock %}