Initial commit: Django gallery project

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 16:47:17 +08:00
commit 8b57a7b66a
53 changed files with 3633 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,217 @@
{% extends 'gallery/base.html' %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- 关于页面头部 -->
<div class="text-center mb-12 animate-fade-in">
<h1 class="text-4xl md:text-5xl font-serif font-bold mb-6">关于画廊</h1>
<p class="text-gray-400 max-w-3xl mx-auto text-lg">
Yitao-Ren Gallery 致力于展示现代摄影艺术的独特魅力,<br>
每一幅作品都是摄影师对世界的独特诠释。
</p>
</div>
<!-- 关于内容 -->
<div class="max-w-4xl mx-auto">
{% if about %}
<!-- 如果有关于页面内容 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 mb-16 animate-slide-up">
<!-- 图片 -->
{% if about.image %}
<div class="relative overflow-hidden rounded-2xl bg-dark-card">
<img
src="{{ about.image.url }}"
alt="{{ about.title }}"
class="w-full h-auto object-cover rounded-2xl"
loading="lazy"
>
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent opacity-0 hover:opacity-100 transition-opacity duration-300"></div>
</div>
{% else %}
<div class="relative overflow-hidden rounded-2xl bg-dark-card flex items-center justify-center h-64">
<div class="text-center">
<svg class="w-16 h-16 mx-auto text-gray-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
<p class="text-gray-500">画廊图片</p>
</div>
</div>
{% endif %}
<!-- 内容 -->
<div class="space-y-6">
<h2 class="text-3xl font-serif font-bold">{{ about.title }}</h2>
<div class="prose prose-invert max-w-none">
<div class="text-gray-300 leading-relaxed whitespace-pre-line">
{{ about.content|linebreaks }}
</div>
</div>
</div>
</div>
{% else %}
<!-- 默认关于内容 -->
<div class="bg-dark-card rounded-2xl p-8 md:p-12 mb-16 animate-slide-up">
<div class="prose prose-invert max-w-none">
<h2 class="text-3xl font-serif font-bold mb-6">YITAO-REN GALLERY</h2>
<div class="space-y-6 text-gray-300">
<p>
成立于2026年Yitao-Ren Gallery 是一个专注于现代摄影艺术展示的在线平台。
我们致力于发现和推广具有独特视角和艺术价值的摄影作品,为艺术家和艺术爱好者
搭建一个交流与展示的空间。
</p>
<p>
画廊的名字来源于创始人对于摄影艺术的热爱与追求。"Yitao"代表着艺术的独特道路,
"Ren"象征着人文关怀。我们相信,每一幅摄影作品都是摄影师与世界对话的方式,
是瞬间与永恒的完美结合。
</p>
<h3 class="text-xl font-semibold mt-8 mb-4 text-gray-200">我们的使命</h3>
<ul class="list-disc pl-6 space-y-2">
<li>展示高质量的现代摄影艺术作品</li>
<li>支持新兴摄影艺术家的发展</li>
<li>促进摄影艺术的交流与传播</li>
<li>为艺术爱好者提供优质的观赏体验</li>
</ul>
<h3 class="text-xl font-semibold mt-8 mb-4 text-gray-200">画廊特色</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-900/50 rounded-lg p-4">
<div class="text-blue-400 font-semibold mb-2">专业策展</div>
<p class="text-sm text-gray-400">每幅作品都经过精心挑选和策展</p>
</div>
<div class="bg-gray-900/50 rounded-lg p-4">
<div class="text-blue-400 font-semibold mb-2">高清展示</div>
<p class="text-sm text-gray-400">提供高质量的作品图片展示</p>
</div>
<div class="bg-gray-900/50 rounded-lg p-4">
<div class="text-blue-400 font-semibold mb-2">艺术家支持</div>
<p class="text-sm text-gray-400">为艺术家提供展示和推广平台</p>
</div>
<div class="bg-gray-900/50 rounded-lg p-4">
<div class="text-blue-400 font-semibold mb-2">社区交流</div>
<p class="text-sm text-gray-400">促进艺术爱好者之间的交流</p>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- 团队介绍 -->
<div class="mb-16">
<h2 class="text-3xl font-serif font-bold text-center mb-12">我们的团队</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="text-center">
<div class="w-32 h-32 mx-auto mb-4 rounded-full bg-gradient-to-br from-blue-600 to-purple-600 flex items-center justify-center">
<span class="text-4xl font-bold text-white">YR</span>
</div>
<h3 class="text-xl font-semibold mb-2">Yitao Ren</h3>
<p class="text-gray-400">创始人 & 策展人</p>
<p class="text-sm text-gray-500 mt-3">拥有10年摄影艺术策展经验</p>
</div>
<div class="text-center">
<div class="w-32 h-32 mx-auto mb-4 rounded-full bg-gradient-to-br from-green-600 to-blue-600 flex items-center justify-center">
<span class="text-4xl font-bold text-white">LT</span>
</div>
<h3 class="text-xl font-semibold mb-2">Li Tao</h3>
<p class="text-gray-400">技术总监</p>
<p class="text-sm text-gray-500 mt-3">负责画廊平台的技术开发与维护</p>
</div>
<div class="text-center">
<div class="w-32 h-32 mx-auto mb-4 rounded-full bg-gradient-to-br from-purple-600 to-pink-600 flex items-center justify-center">
<span class="text-4xl font-bold text-white">AW</span>
</div>
<h3 class="text-xl font-semibold mb-2">Art Wang</h3>
<p class="text-gray-400">艺术顾问</p>
<p class="text-sm text-gray-500 mt-3">资深艺术评论家,摄影艺术专家</p>
</div>
</div>
</div>
<!-- 联系信息 -->
<div class="bg-dark-card rounded-2xl p-8 md:p-12">
<h2 class="text-3xl font-serif font-bold text-center mb-8">联系我们</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="space-y-6">
<div>
<h3 class="text-xl font-semibold mb-3 text-gray-200">画廊地址</h3>
<p class="text-gray-400">
北京市朝阳区艺术大道123号<br>
艺术创意园区A座3层
</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-3 text-gray-200">开放时间</h3>
<p class="text-gray-400">
周二至周日10:00 - 18:00<br>
周一闭馆
</p>
</div>
</div>
<div class="space-y-6">
<div>
<h3 class="text-xl font-semibold mb-3 text-gray-200">联系方式</h3>
<p class="text-gray-400">
电话:+86 10 1234 5678<br>
邮箱info@yitaoren-gallery.com<br>
微信YitaoRenGallery
</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-3 text-gray-200">关注我们</h3>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-blue-400 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"></path>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-blue-400 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"></path>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-blue-400 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z"></path>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% block extra_js %}
<script>
// 平滑滚动到联系信息
document.querySelectorAll('a[href="#contact"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const contactSection = document.querySelector('.bg-dark-card');
if (contactSection) {
contactSection.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// 团队卡片悬停效果
document.querySelectorAll('.text-center').forEach(card => {
card.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-8px)';
this.style.transition = 'transform 0.3s ease';
});
card.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
});
});
</script>
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,63 @@
{% extends "gallery/base.html" %}
{% block title %}登录 - {{ site_name }}{% endblock %}
{% block content %}
<div class="min-h-screen flex items-center justify-center py-12 px-4">
<div class="max-w-md w-full space-y-8">
<div class="text-center">
<h2 class="text-3xl font-bold text-white">登录账户</h2>
<p class="mt-2 text-gray-400">请使用管理员提供的账户登录</p>
</div>
<form method="post" class="mt-8 space-y-6 bg-gray-900 border border-gray-800 rounded-lg p-8">
{% csrf_token %}
{% if messages %}
<div class="space-y-2">
{% for message in messages %}
<div class="p-3 rounded-lg {% if message.tags == 'error' %}bg-red-900/30 border border-red-800 text-red-400{% else %}bg-blue-900/30 border border-blue-800 text-blue-400{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<div class="space-y-4">
<div>
<label for="username" class="block text-gray-300 text-sm mb-2">用户名</label>
<input type="text"
id="username"
name="username"
required
class="w-full px-4 py-3 bg-gray-800 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="请输入用户名">
</div>
<div>
<label for="password" class="block text-gray-300 text-sm mb-2">密码</label>
<input type="password"
id="password"
name="password"
required
class="w-full px-4 py-3 bg-gray-800 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="请输入密码">
</div>
</div>
<div>
<button type="submit"
class="w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition duration-200">
登录
</button>
</div>
<div class="text-center">
<p class="text-gray-500 text-sm">
需要账户?请联系管理员创建
</p>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "gallery/base.html" %}
{% block title %}登出 - {{ site_name }}{% endblock %}
{% block content %}
<div class="min-h-screen flex items-center justify-center py-12 px-4">
<div class="max-w-md w-full space-y-8 text-center">
<div class="bg-gray-900 border border-gray-800 rounded-lg p-8">
<h2 class="text-2xl font-bold text-white mb-4">确认登出</h2>
<p class="text-gray-400 mb-8">您确定要退出登录吗?</p>
<form method="post" class="space-y-4">
{% csrf_token %}
<button type="submit"
class="w-full py-3 px-4 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition duration-200">
确认登出
</button>
<a href="{% url 'index' %}"
class="block py-3 px-4 bg-gray-800 hover:bg-gray-700 text-white font-medium rounded-lg transition duration-200">
取消
</a>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,284 @@
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ page_title|default:site_name }}{% endblock %}</title>
<!-- SEO Meta Tags -->
<meta name="description" content="Yitao-Ren Gallery - 现代摄影作品展示网站">
<meta name="keywords" content="摄影,画廊,艺术作品,现代艺术">
<meta name="author" content="Yitao-Ren Gallery">
<!-- Open Graph Meta Tags -->
<meta property="og:title" content="{% block og_title %}{{ page_title|default:site_name }}{% endblock %}">
<meta property="og:description" content="Yitao-Ren Gallery - 现代摄影作品展示网站">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{% block twitter_title %}{{ page_title|default:site_name }}{% endblock %}">
<meta name="twitter:description" content="Yitao-Ren Gallery - 现代摄影作品展示网站">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Tailwind CSS -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC&family=Playfair+Display&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://gallery.lizexua.com/static/dist/styles.css">
<!-- Custom CSS -->
<style>
/* 自定义滚动条 */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
}
::-webkit-scrollbar-thumb {
background: #3b82f6;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #2563eb;
}
/* 图片懒加载样式 */
.lazy-image {
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.lazy-image.loaded {
opacity: 1;
}
/* 页面过渡动画 */
.page-transition {
animation: fadeIn 0.3s ease-in-out;
}
/* 网格项悬停效果 */
.grid-item {
transition: all 0.3s ease;
}
.grid-item:hover {
transform: translateY(-4px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
}
/* 导航栏下划线动画 */
.nav-link {
position: relative;
}
.nav-link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background-color: #3b82f6;
transition: width 0.3s ease;
}
.nav-link:hover::after {
width: 100%;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body class="bg-dark text-gray-100 font-sans min-h-screen flex flex-col">
<!-- 导航栏 -->
<nav class="fixed top-0 left-0 right-0 bg-black/80 backdrop-blur-sm z-50 border-b border-gray-800">
<div class="container mx-auto px-4 py-3">
<div class="flex items-center justify-between">
<!-- Logo -->
<a href="{% url 'index' %}" class="text-xl font-serif font-bold uppercase tracking-wider hover:text-blue-400 transition-colors">
YITAO-REN GALLERY
</a>
<!-- 导航链接 -->
<div class="hidden md:flex items-center space-x-8">
<a href="{% url 'index' %}" class="nav-link text-gray-300 hover:text-blue-400 transition-colors">
Gallery
</a>
<!-- 分类下拉菜单 -->
{% if categories %}
<div class="relative group">
<button class="nav-link text-gray-300 hover:text-blue-400 transition-colors flex items-center">
Categories
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="absolute top-full left-0 mt-2 w-48 bg-dark-card rounded-lg shadow-xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 border border-gray-800">
{% for category in categories %}
<a href="{% url 'category' category.slug %}" class="block px-4 py-3 text-gray-300 hover:bg-gray-800 hover:text-blue-400 transition-colors border-b border-gray-800 last:border-b-0">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
{% endif %}
<a href="{% url 'about' %}" class="nav-link text-gray-300 hover:text-blue-400 transition-colors">
About
</a>
</div>
<!-- 用户状态(桌面端) -->
<div class="hidden md:flex items-center space-x-4">
{% if user.is_authenticated %}
<!-- 已登录状态 -->
<div class="flex items-center space-x-3">
<div class="w-8 h-8 bg-gray-800 rounded-full flex items-center justify-center">
<span class="text-white text-sm font-medium">{{ user.username|first|upper }}</span>
</div>
<span class="text-gray-300 text-sm">{{ user.username }}</span>
<a href="{% url 'logout' %}"
class="px-3 py-1.5 text-sm bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg transition duration-200">
登出
</a>
</div>
{% else %}
<!-- 未登录状态 -->
<a href="{% url 'login' %}"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition duration-200">
登录
</a>
{% endif %}
</div>
<!-- 移动端菜单按钮 -->
<button id="mobile-menu-button" class="md:hidden text-gray-300 hover:text-blue-400">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
<!-- 移动端菜单 -->
<div id="mobile-menu" class="md:hidden mt-3 hidden">
<div class="flex flex-col space-y-3">
<a href="{% url 'index' %}" class="text-gray-300 hover:text-blue-400 transition-colors py-2">
Gallery
</a>
{% if categories %}
<div class="border-t border-gray-800 pt-2">
<div class="text-gray-400 text-sm mb-2">Categories</div>
{% for category in categories %}
<a href="{% url 'category' category.slug %}" class="block text-gray-300 hover:text-blue-400 transition-colors py-1 pl-4">
{{ category.name }}
</a>
{% endfor %}
</div>
{% endif %}
<a href="{% url 'about' %}" class="text-gray-300 hover:text-blue-400 transition-colors py-2 border-t border-gray-800">
About
</a>
<!-- 用户状态(移动端) -->
<div class="border-t border-gray-800 pt-4">
{% if user.is_authenticated %}
<div class="flex items-center justify-between">
<div class="flex items-center">
<div class="w-8 h-8 bg-gray-800 rounded-full flex items-center justify-center mr-3">
<span class="text-white text-sm font-medium">{{ user.username|first|upper }}</span>
</div>
<span class="text-gray-300 text-sm">{{ user.username }}</span>
</div>
<a href="{% url 'logout' %}"
class="px-3 py-1.5 text-sm bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg transition duration-200">
登出
</a>
</div>
{% else %}
<a href="{% url 'login' %}"
class="block w-full text-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition duration-200">
登录
</a>
{% endif %}
</div>
</div>
</div>
</div>
</nav>
<!-- 主要内容 -->
<main class="flex-grow pt-16 page-transition">
{% block content %}{% endblock %}
</main>
<!-- 页脚 -->
<footer class="bg-black/50 border-t border-gray-800 mt-12">
<div class="container mx-auto px-4 py-8">
<div class="text-center">
<div class="text-gray-400 text-sm mb-2">
{{ copyright_text }}
</div>
<div class="text-gray-500 text-xs">
All artworks are copyrighted by their respective owners.
</div>
</div>
</div>
</footer>
<!-- JavaScript -->
<script>
// 移动端菜单切换
document.getElementById('mobile-menu-button').addEventListener('click', function() {
const menu = document.getElementById('mobile-menu');
menu.classList.toggle('hidden');
});
// 图片懒加载
document.addEventListener('DOMContentLoaded', function() {
const lazyImages = document.querySelectorAll('.lazy-image');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
observer.unobserve(img);
}
});
});
lazyImages.forEach(img => imageObserver.observe(img));
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// 页面加载动画
window.addEventListener('load', function() {
document.body.classList.add('loaded');
});
</script>
{% block extra_js %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,90 @@
{% extends 'gallery/base.html' %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- 面包屑导航 -->
<nav class="mb-8">
<ol class="flex items-center space-x-2 text-sm text-gray-400">
<li>
<a href="{% url 'index' %}" class="hover:text-blue-400 transition-colors">Gallery</a>
</li>
<li>
<span class="mx-2">/</span>
</li>
<li class="text-gray-300">{{ category.name }}</li>
</ol>
</nav>
<!-- 分类标题 -->
<div class="mb-12 text-center">
<h1 class="text-4xl md:text-5xl font-serif font-bold mb-4">{{ category.name }}</h1>
<p class="text-gray-400 max-w-2xl mx-auto">
{{ artworks.count }} 个作品
</p>
</div>
<!-- 作品网格 -->
{% if artworks %}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{% for artwork in artworks %}
<a href="{% url 'artwork_detail' artwork.slug %}" class="group">
<div class="relative overflow-hidden rounded-xl bg-dark-card transition-all duration-300 hover:scale-[1.02] hover:shadow-2xl">
<!-- 图片 -->
<div class="aspect-square overflow-hidden">
<img
data-src="{{ artwork.thumbnail.url|default:artwork.image.url }}"
alt="{{ artwork.title }}"
class="lazy-image w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
loading="lazy"
>
</div>
<!-- 遮罩层 -->
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<!-- 作品信息 -->
<div class="absolute bottom-0 left-0 right-0 p-4 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<div class="bg-black/80 backdrop-blur-sm rounded-lg p-4">
<h3 class="text-lg font-semibold text-white mb-2 truncate">{{ artwork.title }}</h3>
{% if artwork.description %}
<p class="text-gray-300 text-sm line-clamp-2 mb-3">
{{ artwork.description|truncatechars:80 }}
</p>
{% endif %}
<div class="flex items-center justify-between text-xs text-gray-400">
<span>{{ artwork.created_at|date:"Y年m月d日" }}</span>
{% if artwork.view_count %}
<span>{{ artwork.view_count }} 次浏览</span>
{% endif %}
</div>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
{% else %}
<!-- 空状态 -->
<div class="text-center py-20">
<div class="inline-flex items-center justify-center w-24 h-24 rounded-full bg-gray-800/50 mb-6">
<svg class="w-12 h-12 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-300 mb-2">暂无作品</h3>
<p class="text-gray-500 max-w-md mx-auto mb-8">
该分类下暂时没有作品,请稍后再来查看。
</p>
<a href="{% url 'index' %}" class="inline-flex items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
返回 Gallery
</a>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,42 @@
<form method="post" action="{% url 'add_comment' artwork.slug %}" enctype="multipart/form-data" class="mb-8">
{% csrf_token %}
<div class="bg-gray-900 border border-gray-800 rounded-lg p-6">
<h3 class="text-lg font-medium text-white mb-4">发表评论</h3>
<!-- 文本输入 -->
<div class="mb-4">
{{ comment_form.text }}
{% if comment_form.text.errors %}
<div class="mt-2 text-red-400 text-sm">{{ comment_form.text.errors }}</div>
{% endif %}
</div>
<!-- 图片上传 -->
<div class="mb-4">
<label class="block text-gray-400 text-sm mb-2">上传图片(可选)</label>
{{ comment_form.image }}
<p class="text-gray-500 text-xs mt-2">支持 JPG、PNG、GIF、WEBP 格式,最大 5MB</p>
{% if comment_form.image.errors %}
<div class="mt-2 text-red-400 text-sm">{{ comment_form.image.errors }}</div>
{% endif %}
</div>
<!-- 表单错误 -->
{% if comment_form.non_field_errors %}
<div class="mb-4 p-3 bg-red-900/30 border border-red-800 rounded-lg">
{% for error in comment_form.non_field_errors %}
<p class="text-red-400 text-sm">{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<!-- 提交按钮 -->
<div class="flex justify-end">
<button type="submit"
class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition duration-200">
发布评论
</button>
</div>
</div>
</form>

View File

@@ -0,0 +1,35 @@
<div class="bg-gray-900 border border-gray-800 rounded-lg p-6 mb-4">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center">
<div class="w-10 h-10 bg-gray-800 rounded-full flex items-center justify-center mr-3">
<span class="text-white font-medium">{{ comment.user.username|first|upper }}</span>
</div>
<div>
<p class="text-white font-medium">{{ comment.user.username }}</p>
<p class="text-gray-500 text-sm">{{ comment.created_at|date:"Y年m月d日 H:i" }}</p>
</div>
</div>
{% if comment.user == user %}
<form method="post" action="{% url 'delete_comment' comment.pk %}" class="inline">
{% csrf_token %}
<button type="submit"
onclick="return confirm('确定要删除这条评论吗?')"
class="text-red-400 hover:text-red-300 text-sm">
删除
</button>
</form>
{% endif %}
</div>
{% if comment.text %}
<div class="text-white mb-4 leading-relaxed">{{ comment.text|linebreaks }}</div>
{% endif %}
{% if comment.image %}
<div class="mt-4">
<img src="{{ comment.image.url }}"
alt="评论图片"
class="max-w-full h-auto rounded-lg border border-gray-800 max-h-96 object-contain">
</div>
{% endif %}
</div>

View File

@@ -0,0 +1,32 @@
<div class="space-y-4">
{% for comment in comments %}
{% include "gallery/comments/comment_item.html" %}
{% empty %}
<div class="bg-gray-900 border border-gray-800 rounded-lg p-8 text-center">
<p class="text-gray-400">暂无评论,成为第一个评论者吧!</p>
</div>
{% endfor %}
<!-- 分页导航 -->
{% if comments.paginator.num_pages > 1 %}
<div class="flex justify-center items-center space-x-2 mt-8">
{% if comments.has_previous %}
<a href="?page={{ comments.previous_page_number }}#comments"
class="px-4 py-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg">
上一页
</a>
{% endif %}
<span class="text-gray-400">
第 {{ comments.number }} / {{ comments.paginator.num_pages }} 页
</span>
{% if comments.has_next %}
<a href="?page={{ comments.next_page_number }}#comments"
class="px-4 py-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg">
下一页
</a>
{% endif %}
</div>
{% endif %}
</div>

View File

@@ -0,0 +1,337 @@
{% extends 'gallery/base.html' %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- 面包屑导航 -->
<nav class="mb-8">
<ol class="flex items-center space-x-2 text-sm text-gray-400">
<li>
<a href="{% url 'index' %}" class="hover:text-blue-400 transition-colors">Gallery</a>
</li>
<li>
<span class="mx-2">/</span>
</li>
{% if artwork.category %}
<li>
<a href="{% url 'category' artwork.category.slug %}" class="hover:text-blue-400 transition-colors">
{{ artwork.category.name }}
</a>
</li>
<li>
<span class="mx-2">/</span>
</li>
{% endif %}
<li class="text-gray-300">{{ artwork.title }}</li>
</ol>
</nav>
<!-- 返回按钮 -->
<div class="mb-6">
<a href="{% url 'index' %}" class="inline-flex items-center text-blue-400 hover:text-blue-300 transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
返回 Gallery
</a>
</div>
<!-- 作品详情内容 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 animate-fade-in">
<!-- 左侧:图片展示 -->
<div class="space-y-6">
<!-- 主图 -->
<div class="relative overflow-hidden rounded-xl bg-dark-card">
<img
src="{{ artwork.image.url }}"
alt="{{ artwork.title }}"
class="w-full h-auto max-w-full block"
loading="eager"
>
<!-- 图片信息 -->
<div class="absolute bottom-4 left-4 right-4 flex justify-end items-center">
<div class="text-xs text-gray-400 bg-black/70 px-3 py-1 rounded-full backdrop-blur-sm">
{{ artwork.created_at|date:"Y年m月d日" }}
{% if artwork.view_count %}
· {{ artwork.view_count }} 次浏览
{% endif %}
</div>
</div>
</div>
<!-- 缩略图导航(如果有多个图片) -->
{% if prev_artwork or next_artwork %}
<div class="flex justify-between items-center pt-4 border-t border-gray-800">
{% if prev_artwork %}
<a href="{% url 'artwork_detail' prev_artwork.slug %}" class="group flex items-center text-gray-400 hover:text-blue-400 transition-colors">
<svg class="w-5 h-5 mr-2 group-hover:-translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
<div class="text-right">
<div class="text-xs text-gray-500">上一幅</div>
<div class="text-sm">{{ prev_artwork.title|truncatechars:20 }}</div>
</div>
</a>
{% else %}
<div></div>
{% endif %}
{% if next_artwork %}
<a href="{% url 'artwork_detail' next_artwork.slug %}" class="group flex items-center text-gray-400 hover:text-blue-400 transition-colors">
<div class="text-left mr-2">
<div class="text-xs text-gray-500">下一幅</div>
<div class="text-sm">{{ next_artwork.title|truncatechars:20 }}</div>
</div>
<svg class="w-5 h-5 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</a>
{% else %}
<div></div>
{% endif %}
</div>
{% endif %}
</div>
<!-- 右侧:作品信息 -->
<div class="space-y-8">
<!-- 标题和分类 -->
<div>
<h1 class="text-4xl md:text-5xl font-serif font-bold mb-4">{{ artwork.title }}</h1>
{% if artwork.category %}
<div class="inline-flex items-center mb-6">
<a href="{% url 'category' artwork.category.slug %}" class="px-4 py-2 bg-blue-600/20 text-blue-400 rounded-full hover:bg-blue-600/30 transition-colors">
{{ artwork.category.name }}
</a>
<span class="mx-4 text-gray-500"></span>
<span class="text-gray-400">{{ artwork.created_at|date:"Y年m月d日" }}</span>
</div>
{% endif %}
</div>
<!-- 作品描述 -->
<div class="prose prose-invert max-w-none">
<div class="bg-dark-card rounded-xl p-6 border border-gray-800">
<h3 class="text-xl font-semibold mb-4 text-gray-300">作品描述</h3>
<div class="text-gray-300 leading-relaxed whitespace-pre-line">
{{ artwork.description|linebreaks }}
</div>
</div>
</div>
<!-- 作品元数据 -->
<div class="bg-dark-card rounded-xl p-6 border border-gray-800">
<h3 class="text-xl font-semibold mb-4 text-gray-300">作品信息</h3>
<div class="grid grid-cols-2 gap-4">
<div>
<div class="text-sm text-gray-500">作品编号</div>
<div class="text-gray-300 font-mono">ART-{{ artwork.id|stringformat:"04d" }}</div>
</div>
<div>
<div class="text-sm text-gray-500">上传时间</div>
<div class="text-gray-300">{{ artwork.created_at|date:"Y-m-d H:i" }}</div>
</div>
<div>
<div class="text-sm text-gray-500">最后更新</div>
<div class="text-gray-300">{{ artwork.updated_at|date:"Y-m-d H:i" }}</div>
</div>
<div>
<div class="text-sm text-gray-500">浏览次数</div>
<div class="text-gray-300">{{ artwork.view_count }}</div>
</div>
<div>
<div class="text-sm text-gray-500">排序序号</div>
<div class="text-gray-300">{{ artwork.order }}</div>
</div>
</div>
</div>
<!-- 分享和操作 -->
<div class="flex flex-wrap gap-4">
<button onclick="shareArtwork()" class="flex-1 md:flex-none px-6 py-3 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg transition-colors flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"></path>
</svg>
分享作品
</button>
<a href="{{ artwork.image.url }}" download class="flex-1 md:flex-none px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
下载原图
</a>
</div>
</div>
</div>
<!-- 相关作品(同分类) -->
{% if related_artworks %}
<div class="mt-16 pt-12 border-t border-gray-800">
<h2 class="text-2xl font-semibold mb-6">同分类作品</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
{% for related in related_artworks %}
<a href="{% url 'artwork_detail' related.slug %}" class="group">
<div class="relative overflow-hidden rounded-lg bg-dark-card">
<img
data-src="{{ related.thumbnail.url|default:related.image.url }}"
alt="{{ related.title }}"
class="lazy-image w-full h-48 object-cover transition-transform duration-300 group-hover:scale-105"
loading="lazy"
>
<div class="absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-all duration-300"></div>
<div class="p-3">
<h3 class="text-sm font-medium text-gray-300 group-hover:text-blue-400 transition-colors truncate">
{{ related.title }}
</h3>
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- 评论区域 -->
<section class="mt-16 pt-12 border-t border-gray-800" id="comments">
<div class="mb-6">
<h2 class="text-2xl font-bold text-white mb-2">
评论 <span class="text-gray-400">({{ comment_count }})</span>
</h2>
<p class="text-gray-400">分享您对这幅作品的看法</p>
</div>
<!-- 评论表单(登录用户可见) -->
{% if user.is_authenticated %}
{% include "gallery/comments/comment_form.html" %}
{% else %}
<div class="bg-gray-900 border border-gray-800 rounded-lg p-6 mb-8">
<p class="text-white mb-4">请登录后发表评论</p>
<a href="{% url 'login' %}"
class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition duration-200">
立即登录
</a>
</div>
{% endif %}
<!-- 评论列表 -->
{% include "gallery/comments/comment_list.html" %}
</section>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 分享功能
function shareArtwork() {
if (navigator.share) {
navigator.share({
title: '{{ artwork.title|escapejs }} - YITAO-REN GALLERY',
text: '{{ artwork.description|truncatechars:100|escapejs }}',
url: window.location.href,
})
.then(() => console.log('分享成功'))
.catch((error) => console.log('分享失败:', error));
} else {
// 复制链接到剪贴板
navigator.clipboard.writeText(window.location.href)
.then(() => alert('链接已复制到剪贴板!'))
.catch(err => alert('复制失败,请手动复制链接。'));
}
}
// 图片缩放功能
const mainImage = document.querySelector('img[alt="{{ artwork.title }}"]');
if (mainImage) {
mainImage.addEventListener('click', function() {
this.classList.toggle('cursor-zoom-out');
this.classList.toggle('cursor-zoom-in');
this.classList.toggle('scale-150');
this.classList.toggle('origin-center');
});
}
// 键盘导航
document.addEventListener('keydown', function(e) {
if (e.key === 'ArrowLeft' && {% if prev_artwork %}true{% else %}false{% endif %}) {
window.location.href = "{% if prev_artwork %}{% url 'artwork_detail' prev_artwork.slug %}{% endif %}";
} else if (e.key === 'ArrowRight' && {% if next_artwork %}true{% else %}false{% endif %}) {
window.location.href = "{% if next_artwork %}{% url 'artwork_detail' next_artwork.slug %}{% endif %}";
} else if (e.key === 'Escape') {
window.location.href = "{% url 'index' %}";
}
});
// 评论图片预览
const commentImageInput = document.querySelector('input[name="image"]');
if (commentImageInput) {
const imagePreview = document.createElement('div');
imagePreview.className = 'mt-4 hidden';
imagePreview.innerHTML = `
<p class="text-gray-400 text-sm mb-2">图片预览</p>
<img class="max-w-full h-auto rounded-lg border border-gray-800 max-h-48 object-contain">
<button type="button" class="mt-2 text-red-400 hover:text-red-300 text-sm">
移除图片
</button>
`;
commentImageInput.parentNode.insertBefore(imagePreview, commentImageInput.nextSibling);
const previewImg = imagePreview.querySelector('img');
const removeBtn = imagePreview.querySelector('button');
commentImageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
previewImg.src = e.target.result;
imagePreview.classList.remove('hidden');
};
reader.readAsDataURL(file);
} else {
imagePreview.classList.add('hidden');
}
});
removeBtn.addEventListener('click', function() {
commentImageInput.value = '';
imagePreview.classList.add('hidden');
});
}
// 评论表单验证
const commentForm = document.querySelector('form[action*="comment/add"]');
if (commentForm) {
commentForm.addEventListener('submit', function(e) {
const textarea = this.querySelector('textarea[name="text"]');
const fileInput = this.querySelector('input[name="image"]');
if (!textarea.value.trim() && !fileInput.files.length) {
e.preventDefault();
alert('请填写评论内容或上传图片');
textarea.focus();
}
});
}
// 滚动到评论区域如果URL中有#comments
if (window.location.hash === '#comments') {
const commentsSection = document.getElementById('comments');
if (commentsSection) {
setTimeout(() => {
commentsSection.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
}
// 评论删除确认
document.addEventListener('submit', function(e) {
if (e.target.action && e.target.action.includes('comment/delete')) {
if (!confirm('确定要删除这条评论吗?此操作不可撤销。')) {
e.preventDefault();
}
}
});
</script>
{% endblock %}

View File

@@ -0,0 +1,83 @@
{% extends 'gallery/base.html' %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<div class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-serif font-bold mb-4 animate-fade-in">
YITAO-REN GALLERY
</h1>
<p class="text-gray-400 max-w-2xl mx-auto">
探索现代摄影艺术的视觉盛宴,每一幅作品都是时光的印记。
</p>
</div>
<div class="columns-1 md:columns-2 lg:columns-3 xl:columns-4 gap-4 space-y-4 animate-slide-up mx-auto">
{% for artwork in artworks %}
<div class="group relative overflow-hidden rounded-lg bg-dark-card break-inside-avoid mb-4 shadow-lg hover:shadow-xl transition-all duration-300">
<a href="{% url 'artwork_detail' artwork.slug %}" class="block w-full">
<div class="relative w-full">
<img
data-src="{{ artwork.thumbnail.url|default:artwork.image.url }}"
alt="{{ artwork.title }}"
class="lazy-image w-full h-auto object-cover block"
loading="lazy"
style="width: 100%; display: block;"
>
<div class="absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-all duration-300 flex items-center justify-center opacity-0 group-hover:opacity-100">
<div class="transform translate-y-4 group-hover:translate-y-0 transition-all duration-300 text-center p-4">
<h3 class="text-white text-lg font-semibold mb-2 drop-shadow-md">{{ artwork.title }}</h3>
{% if artwork.category %}
<span class="inline-block px-3 py-1 bg-blue-600/90 text-white text-xs rounded-full shadow-sm">
{{ artwork.category.name }}
</span>
{% endif %}
</div>
</div>
</div>
<div class="p-4 md:hidden bg-dark-card">
<h3 class="text-white font-semibold mb-1">{{ artwork.title }}</h3>
{% if artwork.category %}
<span class="text-blue-400 text-sm">{{ artwork.category.name }}</span>
{% endif %}
</div>
</a>
</div>
{% empty %}
<div class="break-inside-avoid w-full text-center py-16 bg-dark-card rounded-lg">
<div class="text-gray-400 mb-4">
<svg class="w-16 h-16 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold mb-2">暂无作品</h3>
<p class="text-gray-400">画廊正在准备中...</p>
</div>
{% endfor %}
</div> <div class="mt-16 pt-8 border-t border-gray-800">
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<div class="text-center">
<div class="text-3xl font-bold text-blue-400 mb-2">{{ artworks.count }}</div>
<div class="text-gray-400">作品总数</div>
</div>
<div class="text-center">
<div class="text-3xl font-bold text-blue-400 mb-2">{{ categories|length }}</div>
<div class="text-gray-400">分类数量</div>
</div>
<div class="text-center">
{% with total_views=artworks.aggregate.total_views %}
<div class="text-3xl font-bold text-blue-400 mb-2">{{ total_views|default:"0" }}</div>
{% endwith %}
<div class="text-gray-400">总浏览次数</div>
</div>
<div class="text-center">
<div class="text-3xl font-bold text-blue-400 mb-2">2025</div>
<div class="text-gray-400">成立年份</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,191 @@
{% extends 'gallery/base.html' %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- 面包屑导航 -->
<nav class="mb-8">
<ol class="flex items-center space-x-2 text-sm text-gray-400">
<li>
<a href="{% url 'index' %}" class="hover:text-blue-400 transition-colors">Gallery</a>
</li>
<li>
<span class="mx-2">/</span>
</li>
<li class="text-gray-300">搜索</li>
</ol>
</nav>
<!-- 搜索框 -->
<div class="max-w-2xl mx-auto mb-12">
<form method="get" action="{% url 'search' %}" class="relative">
<div class="relative">
<input
type="text"
name="q"
value="{{ query }}"
placeholder="搜索作品标题、描述或分类..."
class="w-full px-6 py-4 pl-14 bg-dark-card border border-gray-800 rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
autocomplete="off"
>
<div class="absolute left-5 top-1/2 transform -translate-y-1/2">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<button
type="submit"
class="absolute right-3 top-1/2 transform -translate-y-1/2 px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
>
搜索
</button>
</div>
</form>
{% if query %}
<div class="mt-4 text-center">
<p class="text-gray-400">
搜索 "<span class="text-blue-400 font-medium">{{ query }}</span>" 的结果
<span class="text-gray-300">({{ artworks.count }} 个作品)</span>
</p>
</div>
{% endif %}
</div>
<!-- 搜索结果 -->
{% if query %}
{% if artworks %}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{% for artwork in artworks %}
<a href="{% url 'artwork_detail' artwork.slug %}" class="group">
<div class="relative overflow-hidden rounded-xl bg-dark-card transition-all duration-300 hover:scale-[1.02] hover:shadow-2xl">
<!-- 图片 -->
<div class="aspect-square overflow-hidden">
<img
data-src="{{ artwork.thumbnail.url|default:artwork.image.url }}"
alt="{{ artwork.title }}"
class="lazy-image w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
loading="lazy"
>
</div>
<!-- 遮罩层 -->
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<!-- 作品信息 -->
<div class="absolute bottom-0 left-0 right-0 p-4 transform translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<div class="bg-black/80 backdrop-blur-sm rounded-lg p-4">
<h3 class="text-lg font-semibold text-white mb-2 truncate">{{ artwork.title }}</h3>
{% if artwork.description %}
<p class="text-gray-300 text-sm line-clamp-2 mb-3">
{{ artwork.description|truncatechars:80 }}
</p>
{% endif %}
{% if artwork.category %}
<div class="mb-3">
<a href="{% url 'category' artwork.category.slug %}" class="inline-flex items-center px-3 py-1 bg-blue-600/20 text-blue-400 rounded-full text-xs hover:bg-blue-600/30 transition-colors">
{{ artwork.category.name }}
</a>
</div>
{% endif %}
<div class="flex items-center justify-between text-xs text-gray-400">
<span>{{ artwork.created_at|date:"Y年m月d日" }}</span>
{% if artwork.view_count %}
<span>{{ artwork.view_count }} 次浏览</span>
{% endif %}
</div>
</div>
</div>
<!-- 高亮匹配 -->
{% if query in artwork.title or query in artwork.description %}
<div class="absolute top-4 left-4">
<span class="px-3 py-1 bg-yellow-600/90 text-white text-xs font-medium rounded-full backdrop-blur-sm">
匹配
</span>
</div>
{% endif %}
</div>
</a>
{% endfor %}
</div>
{% else %}
<!-- 无结果 -->
<div class="text-center py-20">
<div class="inline-flex items-center justify-center w-24 h-24 rounded-full bg-gray-800/50 mb-6">
<svg class="w-12 h-12 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-300 mb-2">未找到相关作品</h3>
<p class="text-gray-500 max-w-md mx-auto mb-8">
没有找到与 "<span class="text-blue-400">{{ query }}</span>" 相关的作品。
请尝试其他关键词或浏览所有作品。
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a href="{% url 'index' %}" class="inline-flex items-center justify-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
浏览所有作品
</a>
<button onclick="history.back()" class="inline-flex items-center justify-center px-6 py-3 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg transition-colors">
返回上一页
</button>
</div>
</div>
{% endif %}
{% else %}
<!-- 搜索提示 -->
<div class="text-center py-20">
<div class="inline-flex items-center justify-center w-24 h-24 rounded-full bg-gray-800/50 mb-6">
<svg class="w-12 h-12 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-300 mb-2">搜索作品</h3>
<p class="text-gray-500 max-w-md mx-auto mb-8">
输入关键词搜索作品标题、描述或分类。
支持中文和英文搜索。
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-3xl mx-auto">
<div class="bg-dark-card rounded-xl p-6 border border-gray-800">
<div class="w-12 h-12 rounded-lg bg-blue-600/20 flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"></path>
</svg>
</div>
<h4 class="text-lg font-medium text-gray-300 mb-2">标题搜索</h4>
<p class="text-gray-500 text-sm">
按作品标题关键词搜索,支持模糊匹配。
</p>
</div>
<div class="bg-dark-card rounded-xl p-6 border border-gray-800">
<div class="w-12 h-12 rounded-lg bg-green-600/20 flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
<h4 class="text-lg font-medium text-gray-300 mb-2">描述搜索</h4>
<p class="text-gray-500 text-sm">
搜索作品描述中的关键词,了解作品背后的故事。
</p>
</div>
<div class="bg-dark-card rounded-xl p-6 border border-gray-800">
<div class="w-12 h-12 rounded-lg bg-purple-600/20 flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
</div>
<h4 class="text-lg font-medium text-gray-300 mb-2">分类搜索</h4>
<p class="text-gray-500 text-sm">
按分类名称搜索,快速找到特定类型的作品。
</p>
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}