update. 完成分类显示与详情页显示效果

This commit is contained in:
2026-04-01 18:54:42 +08:00
parent 777ca50328
commit 414dc0ee95
23 changed files with 359 additions and 707 deletions
+14 -3
View File
@@ -83,18 +83,18 @@ class ArticleForm(forms.ModelForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# 核心代码:过滤 section 字段,只保留有父级的分类(即二级分类) # 核心代码:过滤 section 字段,只保留有父级的分类(即二级分类)
# 这里的 queryset 会直接作用于 Admin 的下拉选择框 # 这里的 queryset 会直接作用于 Admin 的下拉选择框
self.fields['section'].queryset = MainMenu.objects.filter(parent__isnull=False, enabled=True) self.fields['section'].queryset = MainMenu.objects.filter(parent__isnull=False, visible=True)
# 可选:优化显示格式,让用户知道是哪个主板块下的子板块 # 可选:优化显示格式,让用户知道是哪个主板块下的子板块
# 例如显示:"[新闻中心] 国内新闻" # 例如显示:"[新闻中心] 国内新闻"
self.fields['section'].label_from_instance = lambda obj: f"[{obj.parent.name}] {obj.name}" self.fields['section'].label_from_instance = lambda obj: f"[{obj.parent.title}] {obj.title}"
# —————— 文章 —————— # —————— 文章 ——————
@admin.register(Article) @admin.register(Article)
class ArticleAdmin(ImportExportModelAdmin): class ArticleAdmin(ImportExportModelAdmin):
form = ArticleForm # <--- 关联自定义表单 form = ArticleForm # <--- 关联自定义表单
list_display = ('title', 'section', 'author', 'is_published', 'created_at') list_display = ('title', 'section', 'author', 'is_published', 'created_at', 'attachment_count')
list_filter = ('section', 'is_published', 'created_at') list_filter = ('section', 'is_published', 'created_at')
search_fields = ('title', 'content') search_fields = ('title', 'content')
date_hierarchy = 'created_at' date_hierarchy = 'created_at'
@@ -108,6 +108,17 @@ class ArticleAdmin(ImportExportModelAdmin):
('发布设置', {'fields': ('is_published',)}), ('发布设置', {'fields': ('is_published',)}),
) )
# 2. 定义显示附件数量的方法
def attachment_count(self, obj):
# 使用 count() 方法统计数量
count = obj.attachments.count()
if count == 0:
return "-" # 没有附件显示横杠
return f"📎 {count}" # 有附件显示图标和数量
# 3. 设置该列在后台的标题
attachment_count.short_description = "附件"
# —————— 通知(带签收) —————— # —————— 通知(带签收) ——————
@admin.register(Notice) @admin.register(Notice)
+43 -3
View File
@@ -39,8 +39,48 @@ def seed_articles(count=20):
section = random.choice(subsections) section = random.choice(subsections)
author = random.choice(users) if users else None author = random.choice(users) if users else None
paragraphs = fake.paragraphs(nb=random.randint(3, 8)) # --- 2. 构建丰富的 HTML 内容 ---
content_html = "\n".join([f"<p>{p}</p>" for p in paragraphs]) html_parts = []
# A. 开头:一段引言或摘要
if random.random() > 0.5:
html_parts.append(f"<p><strong>【摘要】</strong> {fake.sentence(nb_words=10)}</p>")
# B. 正文第一段
html_parts.append(f"<p>{fake.paragraph(nb_sentences=4)}</p>")
# C. 随机插入图片占位符 (模拟上传的封面或插图)
# 使用 placehold.co 生成带文字的灰色占位图
img_width = random.choice([600, 800])
img_height = random.choice([300, 400])
img_text = fake.words(nb=3, unique=True)
html_parts.append(
f'<div style="text-align: center; margin: 20px 0;"><img src="https://placehold.co/{img_width}x{img_height}/23418A/FFF?text={"+".join(img_text)}" style="max-width: 100%; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"></div>')
# D. 正文中间段落
html_parts.append(f"<p>{fake.paragraph(nb_sentences=5)}</p>")
# E. 随机插入引用块 (增加排版层次感)
if random.random() > 0.6:
quote = fake.sentence(nb_words=8)
html_parts.append(
f'<blockquote style="border-left: 5px solid #23418A; padding: 10px 20px; margin: 20px 0; background: #f8f9fa; color: #555;"><i>"{quote}"</i></blockquote>')
# F. 随机插入无序列表 (模拟要点陈述)
if random.random() > 0.7:
list_items = "".join([f"<li>{fake.sentence()}</li>" for _ in range(3)])
html_parts.append(f"<ul style='margin: 20px 0; padding-left: 20px;'>{list_items}</ul>")
# G. 加粗和斜体混合段落
p1 = fake.paragraph(nb_sentences=2)
p2 = fake.paragraph(nb_sentences=2)
html_parts.append(f"<p>{p1} <strong style='color: #23418A;'>{fake.words(nb=4, unique=True)}</strong> {p2}</p>")
# H. 结尾
html_parts.append(f"<p>{fake.paragraph(nb_sentences=3)}</p>")
# 拼接最终 HTML
content_html = "\n".join(html_parts)
article = Article.objects.create( article = Article.objects.create(
title=fake.sentence(nb_words=random.randint(5, 10))[:-1], title=fake.sentence(nb_words=random.randint(5, 10))[:-1],
@@ -81,4 +121,4 @@ def seed_articles(count=20):
if __name__ == "__main__": if __name__ == "__main__":
seed_articles(30) seed_articles(120)
+1 -1
View File
@@ -23,7 +23,7 @@ class MainMenuAdmin(ImportExportModelAdmin):
display_name.admin_order_field = 'name' display_name.admin_order_field = 'name'
def is_enabled(self, obj): def is_enabled(self, obj):
return obj.enabled return obj.visible
is_enabled.boolean = True is_enabled.boolean = True
is_enabled.short_description = '启用' is_enabled.short_description = '启用'
+4 -2
View File
@@ -23,8 +23,9 @@ def show_footer():
} }
@register.inclusion_tag('components/main_menu.html') @register.inclusion_tag('components/main_menu.html', takes_context=True)
def show_main_menu(): def show_main_menu(context):
request = context['request']
# 1. 获取当前生效的那一条页脚数据 # 1. 获取当前生效的那一条页脚数据
# 根据你之前的模型,is_active=True 的只有一条 # 根据你之前的模型,is_active=True 的只有一条
menus = MainMenu.objects.filter(visible=True, parent__isnull=True).order_by('order') menus = MainMenu.objects.filter(visible=True, parent__isnull=True).order_by('order')
@@ -33,4 +34,5 @@ def show_main_menu():
# 3. 将数据传递给模板 # 3. 将数据传递给模板
return { return {
'menus': menus, 'menus': menus,
'request': request,
} }
+2 -1
View File
@@ -12,9 +12,10 @@ urlpatterns = [
path('about/', views.about, name='about'), path('about/', views.about, name='about'),
path('news/', views.news, name='news'), path('news/', views.news, name='news'),
path('info/', views.info, name='info'), path('info/', views.info, name='info'),
path('service/', views.service, name='service'), path('news/<str:sub>.html', views.sub_news_list, name='news_list'),
path('party/', views.party, name='party'), path('party/', views.party, name='party'),
path('interaction/', views.interaction, name='interaction'), path('interaction/', views.interaction, name='interaction'),
path('science/', views.science, name='science'), path('science/', views.science, name='science'),
path('article/<int:pk>.html/', views.article_detail, name='article_detail'), path('article/<int:pk>.html/', views.article_detail, name='article_detail'),
path('news/<int:pk>.html/', views.news_detail, name='news_detail'),
] ]
+59 -80
View File
@@ -9,7 +9,7 @@ from loguru import logger
from article.models import Article from article.models import Article
from base.models import MainMenu from base.models import MainMenu
from news.models import NewsSection from news.models import NewsSection, News
from utils.format import get_now_strftime_format from utils.format import get_now_strftime_format
@@ -32,91 +32,35 @@ def article_detail(request, pk: int):
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return render(request, 'front/error/500.html') return render(request, 'front/error/500.html')
def news_detail(request, pk: int):
"""
根据主键 (pk) 获取文章详情
"""
try:
# 使用 get_object_or_404 简化代码:如果文章不存在,自动返回 404 页面
article = get_object_or_404(News, pk=pk)
# 如果是渲染 HTML 模板
return render(request, 'front/details/news.html', {'article': article})
except (News.DoesNotExist, Http404):
return render(request, 'front/error/404.html')
except Exception as e:
logger.error(e)
logger.error(traceback.format_exc())
return render(request, 'front/error/500.html')
def index(request): def index(request):
# 时间字符串 # 时间字符串
time_str = get_now_strftime_format() time_str = get_now_strftime_format()
# 新闻内容 news_sections = NewsSection.objects.filter(visible=True).order_by('order')
news_data = { news_data = {
"key": "important", "key": "news",
"data": { "data": {
'消防要闻': [ section.title: section.news_set.filter(is_published=True).order_by("-created_at")
{'title': '习近平会见全国应急管理系统先进模范和消防忠诚卫士表彰...', 'date': '2021-11-06'}, for section in news_sections
{'title': '北京市消防救援局挂牌', 'date': '2024-06-19'},
{'title': '市消防救援局即日起开展专项行动,推出一揽子“便民利企”措施', 'date': '2025-07-01'},
{'title': '北京开展石油化工生产装置灭火救援实战演练', 'date': '2025-09-30'},
{'title': '北京市全面启动 2025 年度消防宣传月活动', 'date': '2025-11-11'},
{'title': '逛庙会学消防!厂甸大观园庙会打造消防“打卡点”', 'date': '2026-02-25'},
{'title': '“火焰蓝”守护年味!消防员坚守景区庙会护平安', 'date': '2026-02-21'},
],
'基层动态': [
{'title': '某支队开展冬季练兵比武活动', 'date': '2023-12-01'},
{'title': '社区微型消防站进行应急演练', 'date': '2023-12-05'},
{'title': '朝阳区举办消防安全知识讲座', 'date': '2024-01-15'},
],
'国务院新闻': [
{'title': '国务院安委会部署全国安全生产大检查', 'date': '2023-11-20'},
]
} }
} }
# section_data = [
# {
# "key": "info",
# "data": {
# '信息公开': [
# {'title': '习近平会见全国应急管理系统先进模范和消防忠诚卫士表彰...', 'date': '2021-11-06'},
# {'title': '北京市消防救援局挂牌', 'date': '2024-06-19'},
# {'title': '市消防救援局即日起开展专项行动,推出一揽子“便民利企”措施', 'date': '2025-07-01'},
# {'title': '北京开展石油化工生产装置灭火救援实战演练', 'date': '2025-09-30'},
# {'title': '北京市全面启动 2025 年度消防宣传月活动', 'date': '2025-11-11'},
# {'title': '逛庙会学消防!厂甸大观园庙会打造消防“打卡点”', 'date': '2026-02-25'},
# {'title': '“火焰蓝”守护年味!消防员坚守景区庙会护平安', 'date': '2026-02-21'},
# ],
# },
# },
# {
# "key": "party",
# "data": {
# '党建引领': [
# {'title': '习近平会见全国应急管理系统先进模范和消防忠诚卫士表彰...', 'date': '2021-11-06'},
# {'title': '北京市消防救援局挂牌', 'date': '2024-06-19'},
# {'title': '市消防救援局即日起开展专项行动,推出一揽子“便民利企”措施', 'date': '2025-07-01'},
# {'title': '北京开展石油化工生产装置灭火救援实战演练', 'date': '2025-09-30'},
# {'title': '北京市全面启动 2025 年度消防宣传月活动', 'date': '2025-11-11'},
# {'title': '逛庙会学消防!厂甸大观园庙会打造消防“打卡点”', 'date': '2026-02-25'},
# {'title': '“火焰蓝”守护年味!消防员坚守景区庙会护平安', 'date': '2026-02-21'},
# ],
# },
# },
# {
# "key": "interaction",
# "data": {
# '互动交流': [
# {'title': '习近平会见全国应急管理系统先进模范和消防忠诚卫士表彰...', 'date': '2021-11-06'},
# {'title': '北京市消防救援局挂牌', 'date': '2024-06-19'},
# {'title': '市消防救援局即日起开展专项行动,推出一揽子“便民利企”措施', 'date': '2025-07-01'},
# {'title': '北京开展石油化工生产装置灭火救援实战演练', 'date': '2025-09-30'},
# {'title': '北京市全面启动 2025 年度消防宣传月活动', 'date': '2025-11-11'},
# {'title': '逛庙会学消防!厂甸大观园庙会打造消防“打卡点”', 'date': '2026-02-25'},
# {'title': '“火焰蓝”守护年味!消防员坚守景区庙会护平安', 'date': '2026-02-21'},
# ],
# },
# },
# {
# "key": "science",
# "data": {
# '消防科普': [
# {'title': '习近平会见全国应急管理系统先进模范和消防忠诚卫士表彰...', 'date': '2021-11-06'},
# {'title': '北京市消防救援局挂牌', 'date': '2024-06-19'},
# {'title': '市消防救援局即日起开展专项行动,推出一揽子“便民利企”措施', 'date': '2025-07-01'},
# {'title': '北京开展石油化工生产装置灭火救援实战演练', 'date': '2025-09-30'},
# {'title': '北京市全面启动 2025 年度消防宣传月活动', 'date': '2025-11-11'},
# {'title': '逛庙会学消防!厂甸大观园庙会打造消防“打卡点”', 'date': '2026-02-25'},
# {'title': '“火焰蓝”守护年味!消防员坚守景区庙会护平安', 'date': '2026-02-21'},
# ],
# },
# }
# ]
articles_qs = Article.objects.filter(is_published=True).order_by('-created_at') articles_qs = Article.objects.filter(is_published=True).order_by('-created_at')
main_sections = MainMenu.objects.filter(parent__isnull=True, visible=True).order_by('order').prefetch_related( main_sections = MainMenu.objects.filter(parent__isnull=True, visible=True).order_by('order').prefetch_related(
# 预取可见的子板块 # 预取可见的子板块
@@ -184,12 +128,12 @@ def about(request):
def news(request): def news(request):
sections = NewsSection.objects.filter(enabled=True).all().order_by('order') sections = NewsSection.objects.filter(visible=True).all().order_by('order')
section_data = [ section_data = [
{ {
"key": section.code, "key": section.code,
"data": { "data": {
section.name: section.news_set.filter(is_published=True)[:7], section.title: section.news_set.filter(is_published=True)[:7],
} }
} }
for section in sections for section in sections
@@ -260,6 +204,41 @@ def main_article_list(request, main):
return render(request, 'front/list.html', context=context) return render(request, 'front/list.html', context=context)
def sub_news_list(request, sub: str):
logger.info(f"正在访问 {sub}")
sections = NewsSection.objects.all().order_by('order')
logger.info(f"新闻板块: {sections}")
main_section = {
'title': '新闻中心',
'code': 'news'
}
sub_section = sections.filter(code=sub).first()
logger.info(f"主板块 {main_section} 子板块 {sub_section}")
if not sub_section or not main_section:
raise Http404("不存在的板块")
context = {
'main_section': main_section,
'sub_section': sub_section,
'sections': sections,
'articles': sub_section.news_set.filter(is_published=True).order_by('-created_at')[:21]
}
return render(request, 'front/list.html', context=context)
def main_news_list(request):
logger.info("正在访问新闻中心")
news_list = News.objects.all()
news_sections = NewsSection.objects.all()
return render(request, 'front/list.html', {
'main_section': {
'title': '新闻中心',
'code': 'news'
},
'articles': news_list,
'sections': news_sections,
})
def sub_article_list(request, main: str, sub: str): def sub_article_list(request, main: str, sub: str):
"""信息公开""" """信息公开"""
logger.info(f"正在访问 {main}") logger.info(f"正在访问 {main}")
+5 -5
View File
@@ -11,11 +11,11 @@ from .models import NewsSection, News
# --- 1. 板块管理配置 --- # --- 1. 板块管理配置 ---
@admin.register(NewsSection) @admin.register(NewsSection)
class NewsSectionAdmin(ImportExportModelAdmin): class NewsSectionAdmin(ImportExportModelAdmin):
list_display = ('name', 'code', 'order', 'enabled') # 列表显示的字段 list_display = ('title', 'code', 'order', 'visible') # 列表显示的字段
list_editable = ('order', 'enabled') # 允许直接在列表页修改排序和状态 list_editable = ('order', 'visible') # 允许直接在列表页修改排序和状态
search_fields = ('name', 'code') # 搜索框 search_fields = ('title', 'code') # 搜索框
prepopulated_fields = {'code': ('name',)} # 输入名称时自动生成 Slug prepopulated_fields = {'code': ('title',)} # 输入名称时自动生成 Slug
ordering = ('order', 'name') # 默认排序 ordering = ('order', 'title') # 默认排序
# --- 2. 文章管理配置 --- # --- 2. 文章管理配置 ---
@@ -0,0 +1,27 @@
# Generated by Django 6.0.3 on 2026-04-01 09:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('news', '0002_initial'),
]
operations = [
migrations.AlterModelOptions(
name='newssection',
options={'ordering': ('order', 'title'), 'verbose_name': '新闻模块', 'verbose_name_plural': '新闻模块'},
),
migrations.RenameField(
model_name='newssection',
old_name='name',
new_name='title',
),
migrations.RenameField(
model_name='newssection',
old_name='enabled',
new_name='visible',
),
]
+4 -4
View File
@@ -7,18 +7,18 @@ from users.models import User
# Create your models here. # Create your models here.
class NewsSection(BaseEntity): class NewsSection(BaseEntity):
name = models.CharField('模块名称', max_length=100, unique=True) title = models.CharField('模块名称', max_length=100, unique=True)
code = models.SlugField('唯一标识', max_length=50, unique=True, help_text='用于模板或URL识别,建议英文') code = models.SlugField('唯一标识', max_length=50, unique=True, help_text='用于模板或URL识别,建议英文')
order = models.PositiveSmallIntegerField('排序权重', default=0) order = models.PositiveSmallIntegerField('排序权重', default=0)
enabled = models.BooleanField('启用状态', default=True) visible = models.BooleanField('启用状态', default=True)
class Meta: class Meta:
verbose_name = '新闻模块' verbose_name = '新闻模块'
verbose_name_plural = '新闻模块' verbose_name_plural = '新闻模块'
ordering = ('order', 'name') ordering = ('order', 'title')
def __str__(self): def __str__(self):
return self.name return self.title
class News(BaseEntity): class News(BaseEntity):
+53 -21
View File
@@ -19,49 +19,81 @@ fake = Faker('zh_CN')
def seed_news(count=20): def seed_news(count=20):
print(f"🚀 开始生成 {count} 条新闻数据...") print(f"🚀 开始生成 {count}富文本新闻数据...")
# --- 1. 获取关联数据 --- # --- 1. 获取关联数据 ---
# 获取所有板块
sections = list(NewsSection.objects.all()) sections = list(NewsSection.objects.all())
# 获取所有用户
users = list(User.objects.all()) users = list(User.objects.all())
# 检查基础数据是否存在
if not sections: if not sections:
print("❌ 错误:数据库中没有找到任何板块 (NewsSection)请先添加板块。") print("❌ 错误:数据库中没有找到任何板块 (NewsSection)")
return return
if not users:
print("⚠️ 警告:数据库中没有用户 (User),作者字段将设为空。")
# --- 2. 生成数据 ---
created_count = 0 created_count = 0
for i in range(count): for i in range(count):
# 随机选择一个板块
section = random.choice(sections) section = random.choice(sections)
# 随机选择一个作者 (如果有用户)
author = random.choice(users) if users else None author = random.choice(users) if users else None
# 生成富文本内容 (模拟 CKEditor 输出的 HTML) # --- 2. 构建丰富的 HTML 内容 ---
# paragrahs 生成多段文本,join 拼接 html_parts = []
paragraphs = fake.paragraphs(nb=random.randint(3, 6))
content_html = "\n".join([f"<p>{p}</p>" for p in paragraphs])
# 创建新闻对象 # A. 开头:一段引言或摘要
if random.random() > 0.5:
html_parts.append(f"<p><strong>【摘要】</strong> {fake.sentence(nb_words=10)}</p>")
# B. 正文第一段
html_parts.append(f"<p>{fake.paragraph(nb_sentences=4)}</p>")
# C. 随机插入图片占位符 (模拟上传的封面或插图)
# 使用 placehold.co 生成带文字的灰色占位图
img_width = random.choice([600, 800])
img_height = random.choice([300, 400])
img_text = fake.words(nb=3, unique=True)
html_parts.append(
f'<div style="text-align: center; margin: 20px 0;"><img src="https://placehold.co/{img_width}x{img_height}/23418A/FFF?text={"+".join(img_text)}" style="max-width: 100%; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"></div>')
# D. 正文中间段落
html_parts.append(f"<p>{fake.paragraph(nb_sentences=5)}</p>")
# E. 随机插入引用块 (增加排版层次感)
if random.random() > 0.6:
quote = fake.sentence(nb_words=8)
html_parts.append(
f'<blockquote style="border-left: 5px solid #23418A; padding: 10px 20px; margin: 20px 0; background: #f8f9fa; color: #555;"><i>"{quote}"</i></blockquote>')
# F. 随机插入无序列表 (模拟要点陈述)
if random.random() > 0.7:
list_items = "".join([f"<li>{fake.sentence()}</li>" for _ in range(3)])
html_parts.append(f"<ul style='margin: 20px 0; padding-left: 20px;'>{list_items}</ul>")
# G. 加粗和斜体混合段落
p1 = fake.paragraph(nb_sentences=2)
p2 = fake.paragraph(nb_sentences=2)
html_parts.append(f"<p>{p1} <strong style='color: #23418A;'>{fake.words(nb=4, unique=True)}</strong> {p2}</p>")
# H. 结尾
html_parts.append(f"<p>{fake.paragraph(nb_sentences=3)}</p>")
# 拼接最终 HTML
content_html = "\n".join(html_parts)
# --- 3. 创建对象 ---
news = News.objects.create( news = News.objects.create(
title=fake.sentence(nb_words=random.randint(4, 8))[:-1], # 生成标题并去掉末尾句号 title=fake.sentence(nb_words=random.randint(4, 8))[:-1],
section=section, section=section,
author=author, author=author,
content=content_html, content=content_html,
is_published=True, # 默认已发布 is_published=True,
# 封面图留空,因为自动生成图片文件比较复杂,建议手动上传测试图 # 可以在这里加上摘要字段,如果有的话
# summary=fake.paragraph(nb_sentences=1)
) )
created_count += 1 created_count += 1
print(f" - [{section.name}] {news.title}") print(f" - [{section.title}] {news.title}")
print(f"✅ 成功生成 {created_count} 条新闻数据!") print(f"✅ 成功生成 {created_count}富文本新闻数据!")
if __name__ == "__main__": if __name__ == "__main__":
seed_news(30) seed_news(120)
+1 -1
View File
@@ -47,7 +47,7 @@
{% if items %} {% if items %}
{% for item in items %} {% for item in items %}
<li class="news-item"> <li class="news-item">
<a href="{% url "front:article_detail" pk=item.id %}" <a href="{% url route|default:"front:article_detail" pk=item.id %}"
style="text-decoration: none;color: black;"> style="text-decoration: none;color: black;">
<div class="news-title-wrapper"> <div class="news-title-wrapper">
+2 -1
View File
@@ -2,7 +2,8 @@
{% for menu in menus %} {% for menu in menus %}
<!-- 新闻中心 --> <!-- 新闻中心 -->
<li class="{% if request.resolver_match.kwargs.url == menu.url %}active{% endif %}">
<li class="{% if menu.code in request.path %}active{% endif %}">
<a href="{% url 'front:menu' main=menu.code %}">{{ menu.title }}</a> <a href="{% url 'front:menu' main=menu.code %}">{{ menu.title }}</a>
</li> </li>
{% endfor %} {% endfor %}
+11 -8
View File
@@ -24,7 +24,8 @@
<h1 class="fw-bold mb-3">{{ article.title }}</h1> <h1 class="fw-bold mb-3">{{ article.title }}</h1>
<div class="text-muted small"> <div class="text-muted small">
<span class="me-3"><i class="bi bi-clock"></i> {{ article.created_at|date:"Y-m-d H:i" }}</span> <span class="me-3"><i class="bi bi-clock"></i> {{ article.created_at|date:"Y-m-d H:i" }}</span>
<span class="me-3"><i class="bi bi-person"></i> {{ article.author.username|default:"管理员" }}</span> <span class="me-3"><i
class="bi bi-person"></i> {{ article.author.username|default:"管理员" }}</span>
<span><i class="bi bi-eye"></i> 阅读数:{{ article.views|default:0 }}</span> <span><i class="bi bi-eye"></i> 阅读数:{{ article.views|default:0 }}</span>
</div> </div>
</div> </div>
@@ -32,7 +33,8 @@
<!-- 封面图 (如果有) --> <!-- 封面图 (如果有) -->
{% if article.cover %} {% if article.cover %}
<div class="mb-4 text-center"> <div class="mb-4 text-center">
<img src="{{ article.cover.url }}" alt="{{ article.title }}" class="img-fluid rounded shadow-sm" style="max-height: 400px; width: 100%; object-fit: cover;"> <img src="{{ article.cover.url }}" alt="{{ article.title }}" class="img-fluid rounded shadow-sm"
style="max-height: 400px; width: 100%; object-fit: cover;">
</div> </div>
{% endif %} {% endif %}
@@ -40,17 +42,18 @@
<div class="article-content mb-5" style="line-height: 1.8; font-size: 1.1rem; color: #333;"> <div class="article-content mb-5" style="line-height: 1.8; font-size: 1.1rem; color: #333;">
{{ article.content|safe }} {{ article.content|safe }}
</div> </div>
{% with attachments=article.attachments.all %}
<!-- 3. 附件列表 (如果有附件) --> <!-- 3. 附件列表 (如果有附件) -->
{% if article.attachments_list %} {% if attachments %}
<div class="card bg-light mb-5"> <div class="card bg-light mb-5">
<div class="card-header"> <div class="card-header">
<i class="bi bi-paperclip"></i> 相关附件 <i class="bi bi-paperclip"></i> 相关附件
</div> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
{% for attachment in article.attachments_list %} {% for attachment in attachments %}
<li class="list-group-item bg-transparent"> <li class="list-group-item bg-transparent">
<a href="{{ attachment.file.url }}" target="_blank" class="text-decoration-none"> <a href="{{ attachment.file.url }}" target="_blank"
class="text-decoration-none">
<i class="bi bi-file-earmark-text"></i> <i class="bi bi-file-earmark-text"></i>
<!-- 截取文件名显示,防止太长 --> <!-- 截取文件名显示,防止太长 -->
{{ attachment.file.name|slice:"30:" }} {{ attachment.file.name|slice:"30:" }}
@@ -63,9 +66,9 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{% endwith %}
<!-- 4. 底部操作栏 --> <!-- 4. 底部操作栏 -->
<div class="d-grid gap-4 d-sm-flex justify-content-sm-center border-top pt-4 mt-5"> <div class="d-grid gap-4 d-sm-flex justify-content-sm-center border-top pt-4 mt-5" style="gap: 30px;">
<a href="javascript:history.back()" class="btn btn-outline-secondary btn px-3"> <a href="javascript:history.back()" class="btn btn-outline-secondary btn px-3">
<i class="bi bi-arrow-left"></i> 返回上一页 <i class="bi bi-arrow-left"></i> 返回上一页
</a> </a>
+58 -9
View File
@@ -1,10 +1,59 @@
<!DOCTYPE html> {% extends 'base.html' %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body> {% block title %}{{ article.section.title }} - 长白山消防救援支队{% endblock %}
</html>
{% block content %}
<div class="container mt-4">
<!-- 1. 面包屑导航 -->
<nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important; padding-bottom: 0;">
<li class="breadcrumb-item"><a href="/">网站首页</a></li>
<!-- 动态显示所属板块 -->
<li class="breadcrumb-item"><a href="#">{{ article.section.parent.title }}</a></li>
<li class="breadcrumb-item"><a href="#">{{ article.section.title }}</a></li>
<li class="breadcrumb-item active" aria-current="page">文章详情</li>
</ol>
</nav>
<!-- 2. 文章主体内容 -->
<div class="row justify-content-center mt-4">
<div class="col-lg-10">
<!-- 文章头部信息 -->
<div class="text-center mb-5">
<h1 class="fw-bold mb-3">{{ article.title }}</h1>
<div class="text-muted small">
<span class="me-3"><i class="bi bi-clock"></i> {{ article.created_at|date:"Y-m-d H:i" }}</span>
<span class="me-3"><i
class="bi bi-person"></i> {{ article.author.username|default:"管理员" }}</span>
<span><i class="bi bi-eye"></i> 阅读数:{{ article.views|default:0 }}</span>
</div>
</div>
<!-- 封面图 (如果有) -->
{% if article.cover %}
<div class="mb-4 text-center">
<img src="{{ article.cover.url }}" alt="{{ article.title }}" class="img-fluid rounded shadow-sm"
style="max-height: 400px; width: 100%; object-fit: cover;">
</div>
{% endif %}
<!-- 正文内容 -->
<div class="article-content mb-5" style="line-height: 1.8; font-size: 1.1rem; color: #333;">
{{ article.content|safe }}
</div>
<!-- 4. 底部操作栏 -->
<div class="d-grid gap-4 d-sm-flex justify-content-sm-center border-top pt-4 mt-5" style="gap: 30px;">
<a href="javascript:history.back()" class="btn btn-outline-secondary btn px-3">
<i class="bi bi-arrow-left"></i> 返回上一页
</a>
<a href="/" class="btn btn-primary btn px-3">
<i class="bi bi-house-door-fill"></i> 返回首页
</a>
</div>
</div>
</div>
</div>
{% endblock %}
+2 -1
View File
@@ -42,7 +42,8 @@
{% include 'components/_carousel.html' with carousel_id=carousel.key slides=carousel.data height="300px" %} {% include 'components/_carousel.html' with carousel_id=carousel.key slides=carousel.data height="300px" %}
</div> </div>
<div class="col-md-6 col-sm-12"> <div class="col-md-6 col-sm-12">
{% include 'components/_news_card.html' with data=news_data.data unique_id=news_data.key %} {% url "front:news" as more_url %}
{% include 'components/_news_card.html' with data=news_data.data unique_id=news_data.key more_url=more_url route="front:news_detail" %}
</div> </div>
</div> </div>
+2 -1
View File
@@ -10,7 +10,8 @@
</nav> </nav>
<div class="row" style="margin: 15px auto "> <div class="row" style="margin: 15px auto ">
{% for section in sections %} {% for section in sections %}
<div class="col-6" style="margin: auto auto 15px"> <div class="col-6 {% if forloop.last and not sections|length|divisibleby:2 %}col-md-12{% endif %}"
style="margin: auto auto 15px">
{% url "front:sub_list" main=main_section.code sub=section.key as card_more_url %} {% url "front:sub_list" main=main_section.code sub=section.key as card_more_url %}
{% include 'components/_news_card.html' with data=section.data unique_id=section.key more_url=card_more_url %} {% include 'components/_news_card.html' with data=section.data unique_id=section.key more_url=card_more_url %}
-168
View File
@@ -1,168 +0,0 @@
{% extends 'base.html' %}
{% block title %}互动交流 - 长白山消防救援支队{% endblock %}
{% block content %}
<div class="container">
<nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">互动交流</li>
</ol>
</nav>
<div class="row" style="height: 100%;margin: 15px auto;margin-right: 0 !important;">
<div class="col-3">
<ul class="list-group">
<li class="list-group-item" style="background: #23418A; color: #f5f5f5">
<h5>互动交流</h5>
</li>
{% for sec in sections %}
<li class="list-group-item">
<a href="?q={{ sec }}" style="color: #1d2124;text-decoration: none;"> {{ sec }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-9" style="width: 100%;padding-right: 0 !important;">
<!-- 组件开始 -->
<div class="section-header-card">
<!-- 左侧文字内容 -->
<div class="header-content">
<div class="header-title">
<span class="icon-circle"></span>
{{ q }}
</div>
<hr class="divider-line">
</div>
{# <!-- 右侧装饰色块 -->#}
{# <div class="right-decoration">#}
{# <div class="deco-top"></div>#}
{# <div class="deco-bottom"></div>#}
{# </div>#}
<ul class="list-unstyled mb-0" style="width: 100%;padding-right: 38px; margin-left: 0;">
{# items 就是当前 key 对应的列表数据,直接使用 #}
{% if data_list %}
{% for item in data_list %}
<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>
<!-- 组件结束 -->
<div style="display: flex; justify-content: center; margin-top: 10px;margin-bottom: -5px;">
<ul class="pagination" style="margin: auto;">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<style>
/* --- 组件核心样式 --- */
.section-header-card {
position: relative;
width: 100%;
{#max-width: 800px; /* 模拟图片中的长宽比 */#} min-height: 720px; /* 设置一个保底高度 */
height: auto; /* 高度随内容增加而增加 */
background-color: #ffffff;
border-radius: 12px; /* 大圆角 */
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); /* 柔和阴影 */
overflow: hidden; /* 确保右侧装饰块不溢出圆角 */
display: flex;
flex-direction: column;
align-items: center;
padding-left: 30px;
{#justify-content: space-between;#}
}
/* 左侧内容区域 */
.header-content {
z-index: 2; /* 确保内容在装饰块之上 */
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
/* 标题部分 */
.header-title {
font-size: 20px;
font-weight: 700;
color: #23408A; /* 深蓝色文字 */
margin-bottom: 12px;
display: flex;
align-items: center;
}
/* 空心圆圈图标 */
.icon-circle {
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #23408A; /* 边框颜色同文字 */
border-radius: 50%;
margin-right: 10px;
box-sizing: border-box; /* 确保边框包含在宽高内 */
}
/* 分割线 */
.divider-line {
width: 95%; /* 线条长度,不完全贯穿 */
height: 2px;
background-color: #23408A; /* 线条颜色比文字稍浅一点或保持一致 */
border: none;
margin: 0;
}
/* --- 右侧装饰块 --- */
.right-decoration {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 60px; /* 装饰块宽度 */
display: flex;
flex-direction: column;
}
/* 上方深色块 */
.deco-top {
flex: 1; /* 占据上半部分 */
background-color: #5a6da3; /* 深蓝灰色 */
min-height: 50%;
}
/* 下方浅色块 */
.deco-bottom {
flex: 1; /* 占据下半部分 */
background-color: #eef4ff; /* 极浅的蓝色 */
min-height: 50%;
}
</style>
{% endblock %}
+33 -1
View File
@@ -4,14 +4,38 @@
<div class="container"> <div class="container">
<nav aria-label="breadcrumb" style="margin-left: -10px;"> <nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;"> <ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li> <li class="breadcrumb-item"><a href="/">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ main_section.title }}</li> <li class="breadcrumb-item active" aria-current="page">{{ main_section.title }}</li>
<li class="breadcrumb-item active" aria-current="page">{{ sub_section.title }}</li>
</ol> </ol>
</nav> </nav>
<div class="row" style="height: 100%;margin: 15px auto;margin-right: 0 !important;"> <div class="row" style="height: 100%;margin: 15px auto;margin-right: 0 !important;">
<div class="col-3"> <div class="col-3">
<ul class="list-group"> <ul class="list-group">
{% if request.path|slice:":6" == '/news/' %}
<li class="list-group-item" style="background: #23418A; color: #f5f5f5">
<a href="{% url "front:news" %}"
style="color: #F5F5F5;text-decoration: none;">
<h5 style="margin: 0; padding: 0; line-height: 1.2;">{{ main_section.title }}</h5>
</a>
</li>
{% for sec in sections %}
<!-- 核心修改:判断当前路径是否包含该子栏目的 code -->
{% if request.path|slice:":6" == '/news/' and sec.code in request.path %}
<li class="list-group-item" style="background: #e9ecef;"> <!-- 选中样式:灰色背景 -->
<a href="{% url "front:news_list" sub=sec.code %}"
style="color: #23418A; text-decoration: none; font-weight: bold;"> {{ sec.title }}</a>
</li>
{% else %}
<li class="list-group-item">
<a href="{% url "front:news_list" sub=sec.code %}"
style="color: #1d2124; text-decoration: none;"> {{ sec.title }}</a>
</li>
{% endif %}
{% endfor %}
{% else %}
<li class="list-group-item" style="background: #23418A; color: #f5f5f5"> <li class="list-group-item" style="background: #23418A; color: #f5f5f5">
<a href="{% url "front:main_list" main=main_section.code %}" <a href="{% url "front:main_list" main=main_section.code %}"
style="color: #F5F5F5;text-decoration: none;"> style="color: #F5F5F5;text-decoration: none;">
@@ -19,11 +43,19 @@
</a> </a>
</li> </li>
{% for sec in sections %} {% for sec in sections %}
{% if sec.code in request.path %}
<li class="list-group-item" style="background: #e9ecef;">
<a href="{% url "front:sub_list" main=main_section.code sub=sec.code %}"
style="color: #23418A; text-decoration: none; font-weight: bold;"> {{ sec.title }}</a>
</li>
{% else %}
<li class="list-group-item"> <li class="list-group-item">
<a href="{% url "front:sub_list" main=main_section.code sub=sec.code %}" <a href="{% url "front:sub_list" main=main_section.code sub=sec.code %}"
style="color: #1d2124; text-decoration: none;"> {{ sec.title }}</a> style="color: #1d2124; text-decoration: none;"> {{ sec.title }}</a>
</li> </li>
{% endif %}
{% endfor %} {% endfor %}
{% endif %}
</ul> </ul>
</div> </div>
+3 -2
View File
@@ -7,14 +7,15 @@
<nav aria-label="breadcrumb" style="margin-left: -10px;"> <nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;"> <ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li> <li class="breadcrumb-item"><a href="/">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">新闻中心</li> <li class="breadcrumb-item active" aria-current="page">新闻中心</li>
</ol> </ol>
</nav> </nav>
<div style="margin: 0 5px;"> <div style="margin: 0 5px;">
{% for section in sections %} {% for section in sections %}
<div class="row" style="margin: auto auto 15px;"> <div class="row" style="margin: auto auto 15px;">
{% include 'components/_news_card.html' with data=section.data unique_id=section.key %} {% url "front:news_list" sub=section.key as more_url %}
{% include 'components/_news_card.html' with data=section.data unique_id=section.key more_url=more_url route="front:news_detail" %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
-23
View File
@@ -1,23 +0,0 @@
{% extends 'base.html' %}
{% block title %}党建引领 - 长白山消防救援支队{% endblock %}
{% block content %}
<div class="container">
<nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">党建引领</li>
</ol>
</nav>
<div class="row" style="margin: auto;">
{% for section in sections %}
<div class="col-6" style="margin: auto auto 15px;">
{% if forloop.counter == 2 %}
{% include 'components/_carousel.html' with carousel_id=section.key slides=section.data height="300px" %}
{% else %}
{% include 'components/_news_card.html' with data=section.data unique_id=section.key %}
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endblock %}
-168
View File
@@ -1,168 +0,0 @@
{% extends 'base.html' %}
{% block title %}消防科普 - 长白山消防救援支队{% endblock %}
{% block content %}
<div class="container">
<nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">消防科普</li>
</ol>
</nav>
<div class="row" style="height: 100%;margin: 15px auto;margin-right: 0 !important;">
<div class="col-3">
<ul class="list-group">
<li class="list-group-item" style="background: #23418A; color: #f5f5f5">
<h5>消防科普</h5>
</li>
{% for sec in sections %}
<li class="list-group-item">
<a href="?q={{ sec }}" style="color: #1d2124;text-decoration: none;"> {{ sec }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-9" style="width: 100%;padding-right: 0 !important;">
<!-- 组件开始 -->
<div class="section-header-card">
<!-- 左侧文字内容 -->
<div class="header-content">
<div class="header-title">
<span class="icon-circle"></span>
{{ q }}
</div>
<hr class="divider-line">
</div>
{# <!-- 右侧装饰色块 -->#}
{# <div class="right-decoration">#}
{# <div class="deco-top"></div>#}
{# <div class="deco-bottom"></div>#}
{# </div>#}
<ul class="list-unstyled mb-0" style="width: 100%;padding-right: 38px; margin-left: 0;">
{# items 就是当前 key 对应的列表数据,直接使用 #}
{% if data_list %}
{% for item in data_list %}
<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>
<!-- 组件结束 -->
<div style="display: flex; justify-content: center; margin-top: 10px;margin-bottom: -5px;">
<ul class="pagination" style="margin: auto;">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<style>
/* --- 组件核心样式 --- */
.section-header-card {
position: relative;
width: 100%;
{#max-width: 800px; /* 模拟图片中的长宽比 */#} min-height: 720px; /* 设置一个保底高度 */
height: auto; /* 高度随内容增加而增加 */
background-color: #ffffff;
border-radius: 12px; /* 大圆角 */
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); /* 柔和阴影 */
overflow: hidden; /* 确保右侧装饰块不溢出圆角 */
display: flex;
flex-direction: column;
align-items: center;
padding-left: 30px;
{#justify-content: space-between;#}
}
/* 左侧内容区域 */
.header-content {
z-index: 2; /* 确保内容在装饰块之上 */
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
/* 标题部分 */
.header-title {
font-size: 20px;
font-weight: 700;
color: #23408A; /* 深蓝色文字 */
margin-bottom: 12px;
display: flex;
align-items: center;
}
/* 空心圆圈图标 */
.icon-circle {
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #23408A; /* 边框颜色同文字 */
border-radius: 50%;
margin-right: 10px;
box-sizing: border-box; /* 确保边框包含在宽高内 */
}
/* 分割线 */
.divider-line {
width: 95%; /* 线条长度,不完全贯穿 */
height: 2px;
background-color: #23408A; /* 线条颜色比文字稍浅一点或保持一致 */
border: none;
margin: 0;
}
/* --- 右侧装饰块 --- */
.right-decoration {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 60px; /* 装饰块宽度 */
display: flex;
flex-direction: column;
}
/* 上方深色块 */
.deco-top {
flex: 1; /* 占据上半部分 */
background-color: #5a6da3; /* 深蓝灰色 */
min-height: 50%;
}
/* 下方浅色块 */
.deco-bottom {
flex: 1; /* 占据下半部分 */
background-color: #eef4ff; /* 极浅的蓝色 */
min-height: 50%;
}
</style>
{% endblock %}
-169
View File
@@ -1,169 +0,0 @@
{% extends 'base.html' %}
{% block title %}办事服务 - 长白山消防救援支队{% endblock %}
{% block content %}
<div class="container">
<nav aria-label="breadcrumb" style="margin-left: -10px;">
<ol class="breadcrumb" style="background-color: transparent !important;padding-bottom: 0;">
<li class="breadcrumb-item"><a href="#">网站首页</a></li>
<li class="breadcrumb-item active" aria-current="page">办事服务</li>
</ol>
</nav>
<div class="row" style="height: 100%;margin: auto;margin-right: 0 !important;">
<div class="col-3">
<ul class="list-group">
<li class="list-group-item" style="background: #23418A; color: #f5f5f5">
<h5>办事服务</h5>
</li>
{% for sec in sections %}
<li class="list-group-item">
<a href="?q={{ sec }}" style="color: #1d2124;text-decoration: none;"> {{ sec }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="col-9" style="width: 100%;padding-right: 0 !important;">
<!-- 组件开始 -->
<div class="section-header-card">
<!-- 左侧文字内容 -->
<div class="header-content">
<div class="header-title">
<span class="icon-circle"></span>
{{ q }}
</div>
<hr class="divider-line">
</div>
{# <!-- 右侧装饰色块 -->#}
{# <div class="right-decoration">#}
{# <div class="deco-top"></div>#}
{# <div class="deco-bottom"></div>#}
{# </div>#}
<ul class="list-unstyled mb-0" style="width: 100%;padding-right: 38px; margin-left: 0;">
{# items 就是当前 key 对应的列表数据,直接使用 #}
{% if data_list %}
{% for item in data_list %}
<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>
<!-- 组件结束 -->
<div style="display: flex; justify-content: center; margin-top: 10px;margin-bottom: -5px;">
<ul class="pagination" style="margin: 15px auto;">
<li class="page-item">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<style>
/* --- 组件核心样式 --- */
.section-header-card {
position: relative;
width: 100%;
{#max-width: 800px; /* 模拟图片中的长宽比 */#} min-height: 720px; /* 设置一个保底高度 */
height: auto; /* 高度随内容增加而增加 */
background-color: #ffffff;
border-radius: 12px; /* 大圆角 */
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); /* 柔和阴影 */
overflow: hidden; /* 确保右侧装饰块不溢出圆角 */
display: flex;
flex-direction: column;
align-items: center;
padding-left: 30px;
{#justify-content: space-between;#}
}
/* 左侧内容区域 */
.header-content {
z-index: 2; /* 确保内容在装饰块之上 */
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
/* 标题部分 */
.header-title {
font-size: 20px;
font-weight: 700;
color: #23408A; /* 深蓝色文字 */
margin-bottom: 12px;
display: flex;
align-items: center;
}
/* 空心圆圈图标 */
.icon-circle {
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #23408A; /* 边框颜色同文字 */
border-radius: 50%;
margin-right: 10px;
box-sizing: border-box; /* 确保边框包含在宽高内 */
}
/* 分割线 */
.divider-line {
width: 95%; /* 线条长度,不完全贯穿 */
height: 2px;
background-color: #23408A; /* 线条颜色比文字稍浅一点或保持一致 */
border: none;
margin: 0;
}
/* --- 右侧装饰块 --- */
.right-decoration {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 60px; /* 装饰块宽度 */
display: flex;
flex-direction: column;
}
/* 上方深色块 */
.deco-top {
flex: 1; /* 占据上半部分 */
background-color: #5a6da3; /* 深蓝灰色 */
min-height: 50%;
}
/* 下方浅色块 */
.deco-bottom {
flex: 1; /* 占据下半部分 */
background-color: #eef4ff; /* 极浅的蓝色 */
min-height: 50%;
}
</style>
{% endblock %}
+4 -4
View File
@@ -50,8 +50,8 @@ def upload_image_and_rename(instance, filename):
file_obj = None file_obj = None
# 遍历 instance 的所有属性,找到第一个 FileField 类型的值 # 遍历 instance 的所有属性,找到第一个 FileField 类型的值
for field in instance._meta.get_fields(): for field in instance._meta.get_fields():
if hasattr(field, 'upload_to') and hasattr(instance, field.name): if hasattr(field, 'upload_to') and hasattr(instance, field.title):
val = getattr(instance, field.name) val = getattr(instance, field.title)
if val and hasattr(val, 'chunks'): # 确认是文件对象 if val and hasattr(val, 'chunks'): # 确认是文件对象
file_obj = val file_obj = val
break break
@@ -79,8 +79,8 @@ def upload_file_and_rename(instance, filename):
file_obj = None file_obj = None
# 同样的通用查找逻辑 # 同样的通用查找逻辑
for field in instance._meta.get_fields(): for field in instance._meta.get_fields():
if hasattr(field, 'upload_to') and hasattr(instance, field.name): if hasattr(field, 'upload_to') and hasattr(instance, field.title):
val = getattr(instance, field.name) val = getattr(instance, field.title)
if val and hasattr(val, 'chunks'): if val and hasattr(val, 'chunks'):
file_obj = val file_obj = val
break break