diff --git a/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py new file mode 100644 index 0000000..9314ad7 --- /dev/null +++ b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py @@ -0,0 +1,39 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0002_socialmediasettings_alter_navigationsettings_options_and_more'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.AlterField( + model_name='socialmediasettings', + name='links', + field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('threads', 'Threads'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}), + ), + migrations.CreateModel( + name='BannerSnippet', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(blank=True, help_text='識別用 key(例如 home / category)', max_length=50)), + ('title', models.CharField(blank=True, max_length=255)), + ('link_url', models.URLField(blank=True)), + ('link_text', models.CharField(blank=True, max_length=100)), + ('is_active', models.BooleanField(default=True)), + ('sort_order', models.PositiveIntegerField(default=0)), + ('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'verbose_name': 'Banner', + 'verbose_name_plural': 'Banners', + 'ordering': ['sort_order', 'id'], + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py new file mode 100644 index 0000000..d64738e --- /dev/null +++ b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.7 on 2026-01-09 09:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0003_alter_socialmediasettings_links_bannersnippet'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.RemoveField( + model_name='headersettings', + name='logo', + ), + migrations.AddField( + model_name='headersettings', + name='logo_dark', + field=models.ForeignKey(blank=True, help_text='淺色底用(深色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AddField( + model_name='headersettings', + name='logo_light', + field=models.ForeignKey(blank=True, help_text='深色底用(亮色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + ] diff --git a/innovedus_cms/base/models.py b/innovedus_cms/base/models.py index 99355ee..0824424 100644 --- a/innovedus_cms/base/models.py +++ b/innovedus_cms/base/models.py @@ -29,12 +29,21 @@ from wagtail import blocks @register_setting class HeaderSettings(BaseGenericSetting): - logo = models.ForeignKey( + logo_light = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", + help_text="深色底用(亮色 logo)", + ) + logo_dark = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="淺色底用(深色 logo)", ) site_name = models.CharField(max_length=255, blank=True) extra_links = StreamField([ @@ -47,7 +56,8 @@ class HeaderSettings(BaseGenericSetting): panels = [ MultiFieldPanel( [ - FieldPanel("logo"), + FieldPanel("logo_light"), + FieldPanel("logo_dark"), FieldPanel("site_name"), FieldPanel("extra_links"), ], @@ -82,7 +92,7 @@ class SocialLinkBlock(blocks.StructBlock): ("facebook", "Facebook"), ("twitter", "Twitter"), ("instagram", "Instagram"), - ("thread", "Thread"), + ("threads", "Threads"), ("linkedin", "LinkedIn"), ("youtube", "YouTube"), ] @@ -102,6 +112,40 @@ class SocialMediaSettings(BaseGenericSetting): panels = [FieldPanel("links")] +@register_snippet +class BannerSnippet(models.Model): + key = models.CharField(max_length=50, blank=True, help_text="識別用 key(例如 home / category)") + title = models.CharField(max_length=255, blank=True) + image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=False, + on_delete=models.SET_NULL, + related_name="+", + ) + link_url = models.URLField(blank=True) + link_text = models.CharField(max_length=100, blank=True) + is_active = models.BooleanField(default=True) + sort_order = models.PositiveIntegerField(default=0) + + panels = [ + FieldPanel("key"), + FieldPanel("title"), + FieldPanel("image"), + FieldPanel("link_url"), + FieldPanel("link_text"), + FieldPanel("is_active"), + FieldPanel("sort_order"), + ] + + class Meta: + ordering = ["sort_order", "id"] + verbose_name = "Banner" + verbose_name_plural = "Banners" + + def __str__(self): + return self.title or f"Banner {self.pk}" + @register_snippet class FooterText( DraftStateMixin, @@ -128,4 +172,4 @@ class FooterText( return {"footer_text": self.body} class Meta(TranslatableMixin.Meta): - verbose_name_plural = "Footer Text" \ No newline at end of file + verbose_name_plural = "Footer Text" diff --git a/innovedus_cms/base/templates/base/includes/banner_snippets.html b/innovedus_cms/base/templates/base/includes/banner_snippets.html new file mode 100644 index 0000000..52f6f39 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/banner_snippets.html @@ -0,0 +1,27 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners banner_key as banners %} +{% if banners %} + +{% endif %} diff --git a/innovedus_cms/base/templates/base/includes/home_banner.html b/innovedus_cms/base/templates/base/includes/home_banner.html new file mode 100644 index 0000000..3490cd2 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/home_banner.html @@ -0,0 +1,20 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners "home" first=True as banner %} +{% if banner %} +
+ {% if banner.link_url %} + + {% image banner.image width-1200 alt=banner.title %} + {% if banner.title %} + {{ banner.title }} + {% endif %} + {% if banner.link_text %} + {{ banner.link_text }} + {% endif %} + + {% else %} + {% image banner.image width-1200 alt=banner.title %} + {% endif %} +
+{% endif %} diff --git a/innovedus_cms/base/templatetags/banner_tags.py b/innovedus_cms/base/templatetags/banner_tags.py new file mode 100644 index 0000000..3b39814 --- /dev/null +++ b/innovedus_cms/base/templatetags/banner_tags.py @@ -0,0 +1,15 @@ +from django import template + +from base.models import BannerSnippet + +register = template.Library() + + +@register.simple_tag +def get_banners(key=None, first=False): + banners = BannerSnippet.objects.filter(is_active=True, image__isnull=False) + if key: + banners = banners.filter(key=key) + if first: + return banners.first() + return banners diff --git a/innovedus_cms/home/migrations/0004_articlepage_not_news.py b/innovedus_cms/home/migrations/0004_articlepage_not_news.py new file mode 100644 index 0000000..5bf324e --- /dev/null +++ b/innovedus_cms/home/migrations/0004_articlepage_not_news.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0003_categorypage_latestpage_trendingpage_articlepage_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='articlepage', + name='not_news', + field=models.BooleanField(default=False, help_text='不列入最新消息區塊', verbose_name='Not News'), + ), + ] diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index f062e57..1e5f6b0 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -18,8 +18,9 @@ def _get_env_int(name, default): except ValueError: return default +NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 5) # Default to 5 articles in block layout -HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 8) # Default to 8 articles in horizon layout +HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 8 articles in horizon layout PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination # Mixin for Category-related functionality @@ -85,8 +86,8 @@ class CategoryMixin: # No request means no pagination (e.g., homepage) return { "title": latest_page.title, - "items": ArticlePage.objects.live().order_by("-date")[ - :BLOCK_SIZE + "items": ArticlePage.objects.filter(not_news=False).live().order_by("-date")[ + :NEWS_SIZE ], "url": latest_page.url, } @@ -154,21 +155,24 @@ class HomePage(Page, CategoryMixin): } latest_section = self.get_latest_articles().copy() - latest_section["layout"] = "block" + # latest_section["layout"] = "block" sections["top_section"].append(latest_section) + # NOT NEED TRENDING SECTION CURRENTLY + # ------------------------------------------------------------------ # Exclude latest articles from trending section - latest_items = latest_section.get("items", []) - if hasattr(latest_items, "values_list"): - latest_ids = list(latest_items.values_list("id", flat=True)) - else: - latest_ids = [item.id for item in latest_items] + # latest_items = latest_section.get("items", []) + # if hasattr(latest_items, "values_list"): + # latest_ids = list(latest_items.values_list("id", flat=True)) + # else: + # latest_ids = [item.id for item in latest_items] - trending_section = self.get_trending_articles( - exclude_ids=latest_ids - ).copy() - trending_section["layout"] = "horizon" - sections["top_section"].append(trending_section) + # trending_section = self.get_trending_articles( + # exclude_ids=latest_ids + # ).copy() + # trending_section["layout"] = "horizon" + # sections["top_section"].append(trending_section) + # ------------------------------------------------------------------ # Build category sections categories = CategoryPage.objects.child_of(self).live().in_menu() @@ -271,6 +275,7 @@ class ArticlePage(Page): use_json_field=True, ) trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示") + not_news = models.BooleanField("Not News", default=False, help_text="不列入最新消息區塊") tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True) search_fields = Page.search_fields + [ @@ -281,6 +286,7 @@ class ArticlePage(Page): content_panels = Page.content_panels + [ FieldPanel("trending"), + FieldPanel("not_news"), FieldPanel("cover_image"), FieldPanel("banner_image"), FieldPanel("date"), diff --git a/innovedus_cms/home/static/css/block_list.css b/innovedus_cms/home/static/css/block_list.css new file mode 100644 index 0000000..a29d2e2 --- /dev/null +++ b/innovedus_cms/home/static/css/block_list.css @@ -0,0 +1,79 @@ +.block-list-wrap { + position: relative; +} + +.block-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 36px; + height: 36px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.block-list-arrow[data-dir="left"] { + left: 0; +} + +.block-list-arrow[data-dir="right"] { + right: 0; +} + +.block-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.block-list { + display: flex; + align-items: stretch; + gap: 20px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; +} + +.block-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.block-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; + padding: 16px; +} + +.block-list li img { + width: 100%; + height: 200px; + /* object-fit: contain; */ + display: block; + /* background: #f5f5f5; */ + /* border-radius: 8px; */ +} + +.block-list li a:hover, +.block-list li a:focus { + transform: translateY(-2px); +} + +.block-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/home.css b/innovedus_cms/home/static/css/home.css new file mode 100644 index 0000000..e92c9f7 --- /dev/null +++ b/innovedus_cms/home/static/css/home.css @@ -0,0 +1,74 @@ +.home-page { + max-width: 890px; + margin: 0 auto; + padding: 0 16px; +} + +.home-hero-band { + background-color: #0e1b42; + color: #ffffff; + padding-bottom: 86px; +} + +.home-hero-band a { + color: #ffffff; +} + +.template-homepage .site-header { + background-color: #0e1b42; + color: #ffffff; +} + +.template-homepage .header-inner { + max-width: 1028px; + margin: 0 auto; + /* padding: 0 16px; */ +} + +.template-homepage .main-menu-link { + color: #ffffff; +} + +.template-homepage .header-search .search-input { + /* border-color: #ffffff; */ + background-color: rgba(255, 255, 255, 0.4); +} + +.template-homepage .header-search .search-icon { + color: #ffffff; +} + +.template-homepage .header-search input[type="search"] { + color: #ffffff; +} + +.list-title { + align-items: center; + margin: 10px 0; +} + +.block-title { + display: inline-block; + width: 197px; + height: 87px; + vertical-align: middle; + font-size: 20px; + font-weight: 700; +} + +.block-title span { + padding-left: 21px; + line-height: 87px; +} + +.block-title-divider { + display: inline-flex; + width: 28px; + height: 1px; + transform: translate(-4px, -4px); +} + +.more-link { + text-decoration: none; + font-size: 16px; +} \ No newline at end of file diff --git a/innovedus_cms/home/static/css/horizontal_list.css b/innovedus_cms/home/static/css/horizontal_list.css new file mode 100644 index 0000000..b548f5e --- /dev/null +++ b/innovedus_cms/home/static/css/horizontal_list.css @@ -0,0 +1,112 @@ +.horizontal-list-wrap { + position: relative; +} + +.horizontal-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + width: 52px; + height: 52px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.horizontal-list-arrow[data-dir="left"] { + left: 0; +} + +.horizontal-list-arrow[data-dir="right"] { + right: 0; +} + +.horizontal-list-arrow-icon { + width: 18px; + height: 34px; + display: block; +} + +.horizontal-list-arrow[data-dir="right"] .horizontal-list-arrow-icon { + transform: rotate(180deg); +} + +.horizontal-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.horizontal-list { + display: flex; + align-items: stretch; + gap: 24px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.horizontal-list::-webkit-scrollbar { + display: none; +} + +.horizontal-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.horizontal-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; +} + +.horizontal-list li img { + width: 100%; + height: 200px; + display: block; + margin: 0 3px; +} + +.horizontal-list li a:hover, +.horizontal-list li a:focus { + transform: translateY(-2px); +} + +.horizontal-list li a span { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + font-size: 20px; +} + +.horizontal-list .article-title { + font-size: 20px; +} + +.horizontal-list .article-intro { + font-size: 16px; +} + +.horizontal-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/news_list.css b/innovedus_cms/home/static/css/news_list.css new file mode 100644 index 0000000..798d250 --- /dev/null +++ b/innovedus_cms/home/static/css/news_list.css @@ -0,0 +1,182 @@ +.news-title, .more-news-title { + background-color: #ffffff; + color: #0e1b42; +} + +.news-hero { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "header header" + "image meta" + "image intro" + "image body"; + gap: 16px; + align-items: start; +} + +.news-hero .news-hero-header { + grid-area: header; + display: flex; + align-items: center; + gap: 12px; +} + +.news-hero .list-title { + grid-area: title; + max-width: 310px; + flex: 0 1 310px; + display: block; +} + +.news-hero .fist-news-title { + grid-area: head; + max-width: 580px; + font-size: 40px; + font-weight: 400; + color: #eb9f13; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-hero .first-news-image { + grid-area: image; + position: relative; + border-radius: 10px; + overflow: hidden; +} + +.news-hero .first-news-image::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_b480.png") no-repeat left bottom / cover; + background-size: 480px 25px; + pointer-events: none; +} + +.news-hero .first-news-image img { + display: block; + width:480px; + height:293px; + object-fit:cover; + border-radius: 10px; +} + +.news-hero .fist-news-date { + grid-area: meta; + color: rgba(255, 255, 255, 0.4); +} + +.news-hero .first-news-intro { + grid-area: intro; + font-size: 20px; +} + +.news-hero .first-news-body { + grid-area: body; + font: 12px; +} + +.news-list-items { + display: contents; +} + +.news-list-lower { + margin-top: 24px; + display: flex; + flex-wrap: wrap; + gap: 30px; +} + +.news-list-items a { + display: flex; + flex-direction: column; + gap: 12px; + width: 200px; + text-decoration: none; +} + +.news-list-thumb { + position: relative; + width: 194px; + height: 133px; + border-radius: 10px; + overflow: hidden; +} + +.news-list-thumb::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_o194.png") no-repeat left bottom / cover; + background-size: 194px 25px; + pointer-events: none; +} + +.news-list-thumb img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.news-list-items a span{ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-list-items .article-title { + font-size: 20px; +} + +.news-list-items .article-date { + color: rgba(255, 255, 255, 0.4); +} + +.more-news { + width: 200px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.more-news .article-title { + font-size: 20px; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +@media (max-width: 768px) { + .news-hero { + grid-template-columns: 1fr; + grid-template-areas: + "title" + "image" + "head" + "meta" + "intro" + "body"; + } + + .news-hero .news-hero-header { + display: contents; + } + + .news-hero .list-title { + max-width: none; + } +} diff --git a/innovedus_cms/home/static/img/picfrm_b139.png b/innovedus_cms/home/static/img/picfrm_b139.png new file mode 100644 index 0000000..b5d4f6c Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b139.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b194.png b/innovedus_cms/home/static/img/picfrm_b194.png new file mode 100644 index 0000000..bada8a3 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b194.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b300.png b/innovedus_cms/home/static/img/picfrm_b300.png new file mode 100644 index 0000000..d9d2f5a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b300.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b318.png b/innovedus_cms/home/static/img/picfrm_b318.png new file mode 100644 index 0000000..4b18084 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b318.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b426.png b/innovedus_cms/home/static/img/picfrm_b426.png new file mode 100644 index 0000000..25675e1 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b426.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b480.png b/innovedus_cms/home/static/img/picfrm_b480.png new file mode 100644 index 0000000..494a2d3 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b480.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o139.png b/innovedus_cms/home/static/img/picfrm_o139.png new file mode 100644 index 0000000..916760f Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o139.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o194.png b/innovedus_cms/home/static/img/picfrm_o194.png new file mode 100644 index 0000000..b2f646a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o194.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o300.png b/innovedus_cms/home/static/img/picfrm_o300.png new file mode 100644 index 0000000..045ef82 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o300.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o318.png b/innovedus_cms/home/static/img/picfrm_o318.png new file mode 100644 index 0000000..44ab13a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o318.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o426.png b/innovedus_cms/home/static/img/picfrm_o426.png new file mode 100644 index 0000000..48ca2c7 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o426.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o480.png b/innovedus_cms/home/static/img/picfrm_o480.png new file mode 100644 index 0000000..0406a77 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o480.png differ diff --git a/innovedus_cms/home/static/js/block_list.js b/innovedus_cms/home/static/js/block_list.js new file mode 100644 index 0000000..edd7129 --- /dev/null +++ b/innovedus_cms/home/static/js/block_list.js @@ -0,0 +1,45 @@ +(function () { + function initBlockList(block) { + var list = block.querySelector('.block-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-block-list]'); + blocks.forEach(initBlockList); + }); +})(); diff --git a/innovedus_cms/home/static/js/horizontal_list.js b/innovedus_cms/home/static/js/horizontal_list.js new file mode 100644 index 0000000..3a52dfb --- /dev/null +++ b/innovedus_cms/home/static/js/horizontal_list.js @@ -0,0 +1,45 @@ +(function () { + function initHorizontalList(block) { + var list = block.querySelector('.horizontal-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-horizontal-list]'); + blocks.forEach(initHorizontalList); + }); +})(); diff --git a/innovedus_cms/home/templates/home/home_page.html b/innovedus_cms/home/templates/home/home_page.html index 718cf8d..6af0092 100644 --- a/innovedus_cms/home/templates/home/home_page.html +++ b/innovedus_cms/home/templates/home/home_page.html @@ -1,20 +1,33 @@ {% extends "base.html" %} +{% load static %} {% block body_class %}template-homepage{% endblock %} +{% block extra_css %} + + + + +{% endblock %} {% block content %} -{% with top_section=sections.top_section %} -

- 最新文章 -

- {% for section in top_section %} - {% if section.layout == "block" %} - {% include "home/includes/block_list.html" with items=section.items %} - {% elif section.layout == "horizon" %} - {% include "home/includes/horizontal_list.html" with items=section.items %} - {% endif %} - {% endfor %} -{% endwith %} +
+
+
+ {% include "base/includes/home_banner.html" %} + {% with top_section=sections.top_section %} + {% for section in top_section %} + {% include "home/includes/news_list.html" with section=section %} + {% endfor %} + {% endwith %} +
+
+
- {% for section in sections.category_sections %} - {% include "home/includes/category_session.html" with section=section %} - {% endfor %} -{% endblock content %} \ No newline at end of file +
+ {% for section in sections.category_sections %} + {% include "home/includes/category_session.html" with section=section %} + {% endfor %} +
+{% endblock content %} +{% block extra_js %} + + +{% endblock %} diff --git a/innovedus_cms/home/templates/home/includes/block_list.html b/innovedus_cms/home/templates/home/includes/block_list.html index 1bd06ed..932e07b 100644 --- a/innovedus_cms/home/templates/home/includes/block_list.html +++ b/innovedus_cms/home/templates/home/includes/block_list.html @@ -1,19 +1,23 @@ {% load wagtailimages_tags static %} - +
+ + + +
diff --git a/innovedus_cms/home/templates/home/includes/category_session.html b/innovedus_cms/home/templates/home/includes/category_session.html index e12ec5d..177941d 100644 --- a/innovedus_cms/home/templates/home/includes/category_session.html +++ b/innovedus_cms/home/templates/home/includes/category_session.html @@ -1,13 +1,15 @@ {% load wagtailimages_tags static %}
-

- {% if section.url %} - {{ section.title }} - {% else %} - {{ section.title }} - {% endif %} -

+
+
{{ section.title }}
+ + 查看全部 +
{% if section.layout == "block" %} {% include "home/includes/block_list.html" with items=section.items %} {% elif section.layout == "horizon" %} diff --git a/innovedus_cms/home/templates/home/includes/horizontal_list.html b/innovedus_cms/home/templates/home/includes/horizontal_list.html index 0775831..8f6c7f6 100644 --- a/innovedus_cms/home/templates/home/includes/horizontal_list.html +++ b/innovedus_cms/home/templates/home/includes/horizontal_list.html @@ -1,19 +1,32 @@ {% load wagtailimages_tags static %} - +
+ + + +
diff --git a/innovedus_cms/home/templates/home/includes/news_list.html b/innovedus_cms/home/templates/home/includes/news_list.html new file mode 100644 index 0000000..6479bb0 --- /dev/null +++ b/innovedus_cms/home/templates/home/includes/news_list.html @@ -0,0 +1,82 @@ +{% load wagtailimages_tags static %} + +
+
+
+
+
本日頭條
+ + 查看全部 +
+ {% with first_article=section.items|first %} + {% if first_article %} +
+ {{ first_article.title }} +
+ {% endif %} + {% endwith %} +
+ {% with first_article=section.items|first %} + {% if first_article %} +
+ {{ first_article.date|date:"Y.m.d" }} +
+ + {% if first_article.intro %} +
+ {{ first_article.intro }} +
+ {% endif %} +
+ {{ first_article.body_search_text|truncatechars:120 }} +
+ {% else %} + 目前沒有文章 + {% endif %} + {% endwith %} +
+ + {% if section.items|length >= 2 %} +
+ + +
+
更多頭條
+ {% for article in section.items|slice:"4:8" %} + + {{ article.title }} + + {% endfor %} +
+
+ {% endif %} +
diff --git a/innovedus_cms/mysite/static/css/mysite.css b/innovedus_cms/mysite/static/css/mysite.css index e69de29..fde066a 100644 --- a/innovedus_cms/mysite/static/css/mysite.css +++ b/innovedus_cms/mysite/static/css/mysite.css @@ -0,0 +1,288 @@ +a { + text-decoration: none; +} + +.site-header { + position: relative; + z-index: 10; +} + +.header-inner { + display: flex; + align-items: center; + gap: 24px; + padding: 30px 105px; +} + +.logo { + display: inline-flex; + align-items: center; +} + +.logo--light { + display: none; +} + +.logo--dark { + display: inline-flex; +} + +.template-homepage .logo--light { + display: inline-flex; +} + +.template-homepage .logo--dark { + display: none; +} + +.main-nav { + flex: 1; +} + +.main-menu { + display: flex; + align-items: center; + gap: 24px; + list-style: none; + margin: 0; + padding: 0; +} + +.menu-item { + position: relative; +} + +.main-menu-link { + display: inline-block; + padding: 12px 4px; + font-variation-settings: normal; + color: #0e1b42; + font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif; + word-break: break-word; + font-weight: 400; + font-style: normal; + font-size: 14px; + letter-spacing: 0px; + line-height: normal; + --letter-spacing: 0px; +} + +.submenu { + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + margin-top: -2px; + min-width: 220px; + list-style: none; + padding: 14px 0; + background: #fff; + border-radius: 16px; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 160ms ease, transform 160ms ease; +} + +.menu-item:hover .submenu, +.menu-item:focus-within .submenu { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translateX(-50%) translateY(2px); +} + +.submenu-item a { + display: block; + font-variation-settings: normal; + color: #0e1b42; + font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif; + word-break: break-word; + font-weight: 400; + font-style: normal; + font-size: 14px; + letter-spacing: 0px; + line-height: normal; + --letter-spacing: 0px; + padding: 10px 18px; +} + +.submenu-item a:hover, +.submenu-item a:focus { + background: rgba(0, 0, 0, 0.05); +} + + +.header-search .search-input { + display: flex; + align-items: center; + border-style: solid; + /* border-color: #0e1b42; */ + border-width: 1px; +} + +.header-search .search-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: none; + border: 0; + padding: 0; + cursor: pointer; + color: #1b2140; /* icon 顏色 */ +} + +.header-search .search-icon svg { + width: 20px; + height: 20px; +} + +.header-search input[type="search"] { + border: 0; + background: transparent; + outline: none; + color: #fff; + width: 160px; /* 依需要調 */ +} + +@layer figreset { + :root { + font-family: var( --default-font-family,ui-sans-serif,system-ui,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji' ) + } + + html { + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: antialiased; + width: 100% + } + + body { + margin: 0; + width: 100% + } + + body: has([data-page-overflowx='hidden']) { + overflow-x:hidden + } + + body: has([data-page-overflowx='auto']) { + overflow-x:auto + } +} + +footer { + background: #0e1b42; + color: #ffffff; + padding: 24px 0; + font-size: 14px; + display: flex; + justify-content: center; + gap: 48px; + align-items: flex-start; + text-align: left; +} + +footer .company-info { + max-width: 300px; + margin: 0; + padding: 0 16px; +} + +footer .copyright img { + margin: 10px 0; + width: 265px; + height: 37px; +} + +footer .copyright p { + font-size: 10px; + text-align: left; + margin: 0; +} + +.footer-socials { + display: flex; + /* justify-content: center; */ + gap: 12px; + margin: 16px 0; + flex-wrap: wrap; +} + +.footer-socials .icon { + width: 32px; + height: 32px; + --fill-0: #ffffff; +} + +.footer-socials .icon circle { + fill: #ffffff; + fill-opacity: 0.85; +} + +.footer-socials .icon .icon-cutout { + fill: #0e1b42; +} + +footer .footer-links { + padding: 0 16px; +} + +footer .footer-links li { + list-style-type: none; +} + +footer .footer-links a { + font-size: 14px; + color: #ffffff; +} + +footer .footer-divider { + align-self: stretch; + display: flex; + align-items: center; + padding: 0 8px; + width: 1px; + border-left: 1px solid #ffffff; +} + +footer .footer-sections { + display: flex; + gap: 32px; + flex-wrap: wrap; +} + +@media (max-width: 768px) { + footer { + flex-direction: column; + align-items: center; + text-align: center; + } + + footer .company-info, + footer .footer-links { + max-width: 100%; + } + + .footer-socials { + justify-content: center; + } + + footer .footer-sections { + justify-content: center; + } + + footer .footer-divider { + width: 100%; + padding: 12px 0; + border-left: 0; + border-top: 1px solid #ffffff; + } + + footer .copyright p { + text-align: center; + } +} diff --git a/innovedus_cms/mysite/templates/includes/footer.html b/innovedus_cms/mysite/templates/includes/footer.html index ee5cc23..e01dbe0 100644 --- a/innovedus_cms/mysite/templates/includes/footer.html +++ b/innovedus_cms/mysite/templates/includes/footer.html @@ -1,32 +1,73 @@ {% load navigation_tags %} \ No newline at end of file + + + + + + diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html index c2f028d..febedd7 100644 --- a/innovedus_cms/mysite/templates/includes/header.html +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -1,45 +1,83 @@ {% load wagtailsettings_tags wagtailimages_tags %} {% get_settings use_default_site=True as settings %} -