init. 初始化项目,并初步完成前端模板样式
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
{% comment %}
|
||||
用法: {% include "includes/carousel.html" with carousel_id="mainBanner" slides=carousel_data height="500px" show_caption=True %}
|
||||
|
||||
参数说明:
|
||||
- carousel_id: 唯一ID,用于区分页面上的多个轮播图 (必填)
|
||||
- slides: 列表,包含字典 {'image_url': '...', 'title': '...', 'description': '...'} (必填)
|
||||
- height: 轮播图高度,默认 400px (可选)
|
||||
- show_caption: 是否显示文字描述,默认 True (可选)
|
||||
- object_fit: 图片填充模式,默认 'cover' (可选: cover, contain, fill)
|
||||
{% endcomment %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div id="{{ carousel_id }}" class="carousel slide" data-ride="carousel">
|
||||
|
||||
<!-- 1. 指示器 (Indicators) -->
|
||||
{% if slides|length > 1 %}
|
||||
<ol class="carousel-indicators">
|
||||
{% for slide in slides %}
|
||||
<li data-target="#{{ carousel_id }}" data-slide-to="{{ forloop.counter0 }}"
|
||||
{% if forloop.first %}class="active"{% endif %}></li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
|
||||
<!-- 2. 图片区域 (Inner) -->
|
||||
<div class="carousel-inner" style="height: {{ height|default:'400px' }}; background: #f8f9fa;">
|
||||
{% for slide in slides %}
|
||||
<div class="carousel-item {% if forloop.first %}active{% endif %}" style="height: 100%;">
|
||||
|
||||
<!-- 图片 -->
|
||||
<img src="{{ slide.image_url }}"
|
||||
class="d-block w-100 h-100"
|
||||
alt="{{ slide.title|default:'Slide image' }}"
|
||||
style="object-fit: {{ object_fit|default:'cover' }}; width: 100%; height: 100%;">
|
||||
|
||||
<!-- 文字描述 (可选) -->
|
||||
{% if show_caption|default:True and slide.title %}
|
||||
<div class="carousel-caption d-none d-md-block"
|
||||
style="background: rgba(0,0,0,0.3); border-radius: 8px; padding: 10px 20px; bottom: 20px;">
|
||||
<h5 class="text-white text-shadow">{{ slide.title }}</h5>
|
||||
{% if slide.description %}
|
||||
<p class="text-white text-shadow">{{ slide.description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% empty %}
|
||||
<!-- 空状态占位 -->
|
||||
<div class="carousel-item active" style="height: 100%; display: flex; align-items: center; justify-content: center; color: #999;">
|
||||
<span>暂无轮播图片</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- 3. 控制按钮 (Controls) - 仅当多于1张图时显示 -->
|
||||
{% if slides|length > 1 %}
|
||||
<button class="carousel-control-prev" type="button" data-target="#{{ carousel_id }}" data-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Previous</span>
|
||||
</button>
|
||||
<button class="carousel-control-next" type="button" data-target="#{{ carousel_id }}" data-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="sr-only">Next</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* 可选:增加文字阴影以确保在亮色图片上可见 */
|
||||
.text-shadow {
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
/* 修复 Bootstrap 默认 carousel-item 高度问题 */
|
||||
.carousel-inner {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.carousel-item {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!-- templates/components/_news_card.html -->
|
||||
{% load dict_tools %} {# 确保加载了过滤器,虽然这里可能不再需要 get_item,但保留以防万一 #}
|
||||
|
||||
<div class="news-card">
|
||||
<div class="custom-tabs-header">
|
||||
<div class="tabs-header-wrapper">
|
||||
<ul class="nav nav-tabs" id="newsTab_{{ unique_id }}" role="tablist">
|
||||
{# 核心修改:直接遍历 data 字典的 key #}
|
||||
{% for tab_id, items in data.items %}
|
||||
<li class="nav-item">
|
||||
{# 确定显示名称:如果有 labels 字典则取 labels.tab_id,否则直接用 tab_id (或进行capitalize处理) #}
|
||||
{% with display_name=tab_id %}
|
||||
{# 如果 default 返回的是英文 key,可以在这里做简单的格式化,比如首字母大写,或者保持原样 #}
|
||||
<a class="nav-link {% if forloop.first %}active{% endif %}"
|
||||
id="{{ tab_id }}-tab_{{ unique_id }}"
|
||||
data-toggle="tab"
|
||||
href="#{{ tab_id }}_{{ unique_id }}"
|
||||
role="tab"
|
||||
aria-selected="{% if forloop.first %}true{% else %}false{% endif %}">
|
||||
{{ display_name }}
|
||||
</a>
|
||||
{% endwith %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</ul>
|
||||
<!-- 右侧:更多链接 (独立于 ul) -->
|
||||
<a class="more-link-standalone"
|
||||
href="{{ more_url|default:'#' }}">
|
||||
更多 >
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content news-list-body" id="newsTabContent_{{ unique_id }}">
|
||||
|
||||
{# 再次遍历 data 生成内容面板 #}
|
||||
{% for tab_id, items in data.items %}
|
||||
<div class="tab-pane fade {% if forloop.first %}show active{% endif %}"
|
||||
id="{{ tab_id }}_{{ unique_id }}"
|
||||
role="tabpanel"
|
||||
aria-labelledby="{{ tab_id }}-tab_{{ unique_id }}">
|
||||
|
||||
<ul class="list-unstyled mb-0">
|
||||
{# items 就是当前 key 对应的列表数据,直接使用 #}
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<li class="news-item">
|
||||
<div class="news-title-wrapper">
|
||||
<span class="bullet-point"></span>
|
||||
<span class="news-title-text"
|
||||
title="{{ item.title }}">{{ item.title|truncatechars:30 }}</span>
|
||||
</div>
|
||||
<span class="news-date">{{ item.date }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="text-muted text-center py-2">暂无数据</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user