update. 完成分类显示与详情页显示效果
This commit is contained in:
+14
-3
@@ -83,18 +83,18 @@ class ArticleForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
# 核心代码:过滤 section 字段,只保留有父级的分类(即二级分类)
|
||||
# 这里的 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)
|
||||
class ArticleAdmin(ImportExportModelAdmin):
|
||||
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')
|
||||
search_fields = ('title', 'content')
|
||||
date_hierarchy = 'created_at'
|
||||
@@ -108,6 +108,17 @@ class ArticleAdmin(ImportExportModelAdmin):
|
||||
('发布设置', {'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)
|
||||
|
||||
+43
-3
@@ -39,8 +39,48 @@ def seed_articles(count=20):
|
||||
section = random.choice(subsections)
|
||||
author = random.choice(users) if users else None
|
||||
|
||||
paragraphs = fake.paragraphs(nb=random.randint(3, 8))
|
||||
content_html = "\n".join([f"<p>{p}</p>" for p in paragraphs])
|
||||
# --- 2. 构建丰富的 HTML 内容 ---
|
||||
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(
|
||||
title=fake.sentence(nb_words=random.randint(5, 10))[:-1],
|
||||
@@ -81,4 +121,4 @@ def seed_articles(count=20):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed_articles(30)
|
||||
seed_articles(120)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ class MainMenuAdmin(ImportExportModelAdmin):
|
||||
display_name.admin_order_field = 'name'
|
||||
|
||||
def is_enabled(self, obj):
|
||||
return obj.enabled
|
||||
return obj.visible
|
||||
|
||||
is_enabled.boolean = True
|
||||
is_enabled.short_description = '启用'
|
||||
|
||||
@@ -23,8 +23,9 @@ def show_footer():
|
||||
}
|
||||
|
||||
|
||||
@register.inclusion_tag('components/main_menu.html')
|
||||
def show_main_menu():
|
||||
@register.inclusion_tag('components/main_menu.html', takes_context=True)
|
||||
def show_main_menu(context):
|
||||
request = context['request']
|
||||
# 1. 获取当前生效的那一条页脚数据
|
||||
# 根据你之前的模型,is_active=True 的只有一条
|
||||
menus = MainMenu.objects.filter(visible=True, parent__isnull=True).order_by('order')
|
||||
@@ -33,4 +34,5 @@ def show_main_menu():
|
||||
# 3. 将数据传递给模板
|
||||
return {
|
||||
'menus': menus,
|
||||
'request': request,
|
||||
}
|
||||
|
||||
+2
-1
@@ -12,9 +12,10 @@ urlpatterns = [
|
||||
path('about/', views.about, name='about'),
|
||||
path('news/', views.news, name='news'),
|
||||
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('interaction/', views.interaction, name='interaction'),
|
||||
path('science/', views.science, name='science'),
|
||||
path('article/<int:pk>.html/', views.article_detail, name='article_detail'),
|
||||
path('news/<int:pk>.html/', views.news_detail, name='news_detail'),
|
||||
]
|
||||
|
||||
+59
-80
@@ -9,7 +9,7 @@ from loguru import logger
|
||||
|
||||
from article.models import Article
|
||||
from base.models import MainMenu
|
||||
from news.models import NewsSection
|
||||
from news.models import NewsSection, News
|
||||
from utils.format import get_now_strftime_format
|
||||
|
||||
|
||||
@@ -32,91 +32,35 @@ def article_detail(request, pk: int):
|
||||
logger.error(traceback.format_exc())
|
||||
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):
|
||||
# 时间字符串
|
||||
time_str = get_now_strftime_format()
|
||||
# 新闻内容
|
||||
news_sections = NewsSection.objects.filter(visible=True).order_by('order')
|
||||
news_data = {
|
||||
"key": "important",
|
||||
"key": "news",
|
||||
"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'},
|
||||
],
|
||||
'基层动态': [
|
||||
{'title': '某支队开展冬季练兵比武活动', 'date': '2023-12-01'},
|
||||
{'title': '社区微型消防站进行应急演练', 'date': '2023-12-05'},
|
||||
{'title': '朝阳区举办消防安全知识讲座', 'date': '2024-01-15'},
|
||||
],
|
||||
'国务院新闻': [
|
||||
{'title': '国务院安委会部署全国安全生产大检查', 'date': '2023-11-20'},
|
||||
]
|
||||
section.title: section.news_set.filter(is_published=True).order_by("-created_at")
|
||||
for section in news_sections
|
||||
}
|
||||
}
|
||||
# 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')
|
||||
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):
|
||||
sections = NewsSection.objects.filter(enabled=True).all().order_by('order')
|
||||
sections = NewsSection.objects.filter(visible=True).all().order_by('order')
|
||||
section_data = [
|
||||
{
|
||||
"key": section.code,
|
||||
"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
|
||||
@@ -260,6 +204,41 @@ def main_article_list(request, main):
|
||||
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):
|
||||
"""信息公开"""
|
||||
logger.info(f"正在访问 {main}")
|
||||
|
||||
+5
-5
@@ -11,11 +11,11 @@ from .models import NewsSection, News
|
||||
# --- 1. 板块管理配置 ---
|
||||
@admin.register(NewsSection)
|
||||
class NewsSectionAdmin(ImportExportModelAdmin):
|
||||
list_display = ('name', 'code', 'order', 'enabled') # 列表显示的字段
|
||||
list_editable = ('order', 'enabled') # 允许直接在列表页修改排序和状态
|
||||
search_fields = ('name', 'code') # 搜索框
|
||||
prepopulated_fields = {'code': ('name',)} # 输入名称时自动生成 Slug
|
||||
ordering = ('order', 'name') # 默认排序
|
||||
list_display = ('title', 'code', 'order', 'visible') # 列表显示的字段
|
||||
list_editable = ('order', 'visible') # 允许直接在列表页修改排序和状态
|
||||
search_fields = ('title', 'code') # 搜索框
|
||||
prepopulated_fields = {'code': ('title',)} # 输入名称时自动生成 Slug
|
||||
ordering = ('order', 'title') # 默认排序
|
||||
|
||||
|
||||
# --- 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
@@ -7,18 +7,18 @@ from users.models import User
|
||||
|
||||
# Create your models here.
|
||||
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识别,建议英文')
|
||||
order = models.PositiveSmallIntegerField('排序权重', default=0)
|
||||
enabled = models.BooleanField('启用状态', default=True)
|
||||
visible = models.BooleanField('启用状态', default=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '新闻模块'
|
||||
verbose_name_plural = '新闻模块'
|
||||
ordering = ('order', 'name')
|
||||
ordering = ('order', 'title')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.title
|
||||
|
||||
|
||||
class News(BaseEntity):
|
||||
|
||||
+53
-21
@@ -19,49 +19,81 @@ fake = Faker('zh_CN')
|
||||
|
||||
|
||||
def seed_news(count=20):
|
||||
print(f"🚀 开始生成 {count} 条新闻数据...")
|
||||
print(f"🚀 开始生成 {count} 条富文本新闻数据...")
|
||||
|
||||
# --- 1. 获取关联数据 ---
|
||||
# 获取所有板块
|
||||
sections = list(NewsSection.objects.all())
|
||||
# 获取所有用户
|
||||
users = list(User.objects.all())
|
||||
|
||||
# 检查基础数据是否存在
|
||||
if not sections:
|
||||
print("❌ 错误:数据库中没有找到任何板块 (NewsSection)!请先添加板块。")
|
||||
print("❌ 错误:数据库中没有找到任何板块 (NewsSection)!")
|
||||
return
|
||||
if not users:
|
||||
print("⚠️ 警告:数据库中没有用户 (User),作者字段将设为空。")
|
||||
|
||||
# --- 2. 生成数据 ---
|
||||
created_count = 0
|
||||
|
||||
for i in range(count):
|
||||
# 随机选择一个板块
|
||||
section = random.choice(sections)
|
||||
# 随机选择一个作者 (如果有用户)
|
||||
author = random.choice(users) if users else None
|
||||
|
||||
# 生成富文本内容 (模拟 CKEditor 输出的 HTML)
|
||||
# paragrahs 生成多段文本,join 拼接
|
||||
paragraphs = fake.paragraphs(nb=random.randint(3, 6))
|
||||
content_html = "\n".join([f"<p>{p}</p>" for p in paragraphs])
|
||||
# --- 2. 构建丰富的 HTML 内容 ---
|
||||
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)
|
||||
|
||||
# --- 3. 创建对象 ---
|
||||
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,
|
||||
author=author,
|
||||
content=content_html,
|
||||
is_published=True, # 默认已发布
|
||||
# 封面图留空,因为自动生成图片文件比较复杂,建议手动上传测试图
|
||||
is_published=True,
|
||||
# 可以在这里加上摘要字段,如果有的话
|
||||
# summary=fake.paragraph(nb_sentences=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__":
|
||||
seed_news(30)
|
||||
seed_news(120)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<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;">
|
||||
|
||||
<div class="news-title-wrapper">
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
{% 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>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
<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 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>
|
||||
@@ -32,7 +33,8 @@
|
||||
<!-- 封面图 (如果有) -->
|
||||
{% 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;">
|
||||
<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 %}
|
||||
|
||||
@@ -40,17 +42,18 @@
|
||||
<div class="article-content mb-5" style="line-height: 1.8; font-size: 1.1rem; color: #333;">
|
||||
{{ article.content|safe }}
|
||||
</div>
|
||||
|
||||
{% with attachments=article.attachments.all %}
|
||||
<!-- 3. 附件列表 (如果有附件) -->
|
||||
{% if article.attachments_list %}
|
||||
{% if attachments %}
|
||||
<div class="card bg-light mb-5">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-paperclip"></i> 相关附件
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for attachment in article.attachments_list %}
|
||||
{% for attachment in attachments %}
|
||||
<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>
|
||||
<!-- 截取文件名显示,防止太长 -->
|
||||
{{ attachment.file.name|slice:"30:" }}
|
||||
@@ -63,9 +66,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endwith %}
|
||||
<!-- 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">
|
||||
<i class="bi bi-arrow-left"></i> 返回上一页
|
||||
</a>
|
||||
|
||||
@@ -1,10 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends 'base.html' %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% block title %}{{ article.section.title }} - 长白山消防救援支队{% endblock %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
{% include 'components/_carousel.html' with carousel_id=carousel.key slides=carousel.data height="300px" %}
|
||||
</div>
|
||||
<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>
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
</nav>
|
||||
<div class="row" style="margin: 15px auto ">
|
||||
{% 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 %}
|
||||
|
||||
{% include 'components/_news_card.html' with data=section.data unique_id=section.key more_url=card_more_url %}
|
||||
|
||||
@@ -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">«</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">»</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,14 +4,38 @@
|
||||
<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"><a href="/">网站首页</a></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>
|
||||
</nav>
|
||||
|
||||
<div class="row" style="height: 100%;margin: 15px auto;margin-right: 0 !important;">
|
||||
<div class="col-3">
|
||||
<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">
|
||||
<a href="{% url "front:main_list" main=main_section.code %}"
|
||||
style="color: #F5F5F5;text-decoration: none;">
|
||||
@@ -19,11 +43,19 @@
|
||||
</a>
|
||||
</li>
|
||||
{% 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">
|
||||
<a href="{% url "front:sub_list" main=main_section.code sub=sec.code %}"
|
||||
style="color: #1d2124; text-decoration: none;"> {{ sec.title }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
|
||||
<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="/">网站首页</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">新闻中心</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div style="margin: 0 5px;">
|
||||
{% for section in sections %}
|
||||
<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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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">«</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">»</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 %}
|
||||
@@ -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">«</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">»</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
@@ -50,8 +50,8 @@ def upload_image_and_rename(instance, filename):
|
||||
file_obj = None
|
||||
# 遍历 instance 的所有属性,找到第一个 FileField 类型的值
|
||||
for field in instance._meta.get_fields():
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
|
||||
val = getattr(instance, field.name)
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.title):
|
||||
val = getattr(instance, field.title)
|
||||
if val and hasattr(val, 'chunks'): # 确认是文件对象
|
||||
file_obj = val
|
||||
break
|
||||
@@ -79,8 +79,8 @@ def upload_file_and_rename(instance, filename):
|
||||
file_obj = None
|
||||
# 同样的通用查找逻辑
|
||||
for field in instance._meta.get_fields():
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
|
||||
val = getattr(instance, field.name)
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.title):
|
||||
val = getattr(instance, field.title)
|
||||
if val and hasattr(val, 'chunks'):
|
||||
file_obj = val
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user