feat: add pagination to cities and arts index

- Implement pagination for the cities index view.
- Add shared pagination partial to reduce code duplication.
- Modify arts index view to utilize the new pagination.
- Update cities controller to include pagination logic.

These updates improve usability by allowing better navigation through larger datasets, ensuring users can easily access and view items across multiple pages.
This commit is contained in:
songtianlun 2025-01-23 17:30:05 +08:00
parent a4de04874d
commit f33fb4d2ba
4 changed files with 152 additions and 80 deletions

View File

@ -1,6 +1,5 @@
class CitiesController < ApplicationController class CitiesController < ApplicationController
def index def index
@cities = City.all.order(:name)
@regions = Region.includes(:countries).order(:name) @regions = Region.includes(:countries).order(:name)
@cities = City.includes(:country, country: :region).active.order(:name) @cities = City.includes(:country, country: :region).active.order(:name)
@ -13,6 +12,8 @@ class CitiesController < ApplicationController
@current_country = Country.friendly.find(params[:country]) @current_country = Country.friendly.find(params[:country])
@cities = @cities.by_country(@current_country.id) @cities = @cities.by_country(@current_country.id)
end end
@cities = @cities.page(params[:page]).per(10)
end end
def show def show

View File

@ -101,87 +101,72 @@
</div> </div>
</div> </div>
<!-- 图片网格 --> <div class="container mx-auto px-4 pb-16">
<div class="container mx-auto px-4 pb-16"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <% @weather_arts.each do |art| %>
<% @weather_arts.each do |art| %> <div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 group overflow-hidden">
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 group overflow-hidden"> <figure class="relative aspect-square overflow-hidden">
<!-- 图片部分 --> <% if art.image.attached? %>
<figure class="relative aspect-square overflow-hidden"> <%= image_tag art.image,
<% if art.image.attached? %> class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %>
<%= image_tag art.image,
class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %>
<!-- 渐变遮罩 --> <div class="absolute inset-0 bg-gradient-to-t from-black via-black/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div class="absolute inset-0 bg-gradient-to-t from-black via-black/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<!-- 悬停信息 --> <div class="absolute inset-0 p-6 flex flex-col justify-end translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition-all duration-300">
<div class="absolute inset-0 p-6 flex flex-col justify-end translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition-all duration-300"> <div class="text-white space-y-2">
<div class="text-white space-y-2"> <h3 class="text-xl font-display font-bold">
<h3 class="text-xl font-display font-bold"> <%= art.city.name %>
</h3>
<p class="text-sm text-white/80">
<%= art.city.country.name %>
</p>
<div class="flex items-center gap-2 text-white/90">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
</svg>
<%= art.description %>
</div>
</div>
</div>
<% end %>
</figure>
<!-- 信息部分 -->
<div class="card-body p-4">
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-display font-bold leading-tight">
<%= art.city.name %> <%= art.city.name %>
</h3> </h3>
<p class="text-sm text-white/80"> <p class="text-sm text-base-content/70">
<%= art.city.country.name %> <%= art.weather_date.strftime("%B %d, %Y") %>
</p> </p>
<div class="flex items-center gap-2 text-white/90"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="text-right">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" /> <div class="text-2xl font-bold text-primary">
</svg> <%= art.temperature %>°C
<%= art.description %> </div>
<div class="text-sm text-base-content/70">
<%= art.humidity %>% humidity
</div> </div>
</div> </div>
</div> </div>
<% end %>
</figure>
<!-- 信息部分 --> <%= link_to city_weather_art_path(art.city, art),
<div class="card-body p-4"> class: "btn btn-primary btn-sm w-full" do %>
<div class="flex justify-between items-start mb-3"> View Details
<div> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<h3 class="font-display font-bold leading-tight"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
<%= art.city.name %> </svg>
</h3> <% end %>
<p class="text-sm text-base-content/70">
<%= art.weather_date.strftime("%B %d, %Y") %>
</p>
</div>
<div class="text-right">
<div class="text-2xl font-bold text-primary">
<%= art.temperature %>°C
</div>
<div class="text-sm text-base-content/70">
<%= art.humidity %>% humidity
</div>
</div>
</div> </div>
<%= link_to city_weather_art_path(art.city, art),
class: "btn btn-primary btn-sm w-full" do %>
View Details
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
<% end %>
</div> </div>
</div> <% end %>
<% end %>
</div>
<!-- 分页 -->
<% if @weather_arts.total_pages > 1 %>
<div class="flex justify-center mt-12">
<div class="btn-group">
<%= link_to_prev_page @weather_arts, "Previous",
class: "btn btn-outline #{'btn-disabled' unless @weather_arts.prev_page}" %>
<% @weather_arts.total_pages.times do |i| %>
<%= link_to i + 1, arts_path(page: i + 1, region: params[:region], sort: params[:sort]),
class: "btn btn-outline #{'btn-active' if @weather_arts.current_page == i + 1}" %>
<% end %>
<%= link_to_next_page @weather_arts, "Next",
class: "btn btn-outline #{'btn-disabled' unless @weather_arts.next_page}" %>
</div>
</div> </div>
<% end %>
<%= render 'shared/pagination',
collection: @weather_arts,
collection_name: 'weather arts' %>
</div>
</div> </div>
</div> </div>

View File

@ -12,7 +12,6 @@
</div> </div>
<% end %> <% end %>
<!-- 标题内容 -->
<div class="relative pt-24 pb-32"> <div class="relative pt-24 pb-32">
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
<div class="max-w-3xl mx-auto text-center space-y-6"> <div class="max-w-3xl mx-auto text-center space-y-6">
@ -38,13 +37,10 @@
</div> </div>
</div> </div>
<!-- 筛选导航 - 使用下拉菜单 -->
<div class="sticky top-16 z-20 bg-base-100/95 backdrop-blur-sm border-b border-base-200"> <div class="sticky top-16 z-20 bg-base-100/95 backdrop-blur-sm border-b border-base-200">
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
<div class="py-3 flex items-center justify-between gap-4"> <div class="py-3 flex items-center justify-between gap-4">
<!-- 左侧筛选器 -->
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<!-- 区域选择下拉框 -->
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-ghost gap-2"> <button class="btn btn-ghost gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@ -73,7 +69,6 @@
</ul> </ul>
</div> </div>
<!-- 国家选择下拉框 (如果选择了区域) -->
<% if @current_region %> <% if @current_region %>
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-ghost gap-2"> <button class="btn btn-ghost gap-2">
@ -104,7 +99,6 @@
<% end %> <% end %>
</div> </div>
<!-- 右侧结果统计 -->
<div class="text-sm text-base-content/70"> <div class="text-sm text-base-content/70">
<%= @cities.count %> <%= 'city'.pluralize(@cities.count) %> <%= @cities.count %> <%= 'city'.pluralize(@cities.count) %>
<% if @current_country %> <% if @current_country %>
@ -117,10 +111,16 @@
</div> </div>
</div> </div>
<!-- 城市网格 -->
<div class="container mx-auto px-4 py-8"> <div class="container mx-auto px-4 py-8">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> <div class="container mx-auto px-4 pb-16">
<%= render partial: 'city', collection: @cities %> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<%= render partial: 'city', collection: @cities %>
</div>
<%= render 'shared/pagination',
collection: @cities,
collection_name: 'cities' %>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,86 @@
<%# app/views/shared/_pagination.html.erb %>
<% if collection.total_pages > 1 %>
<div class="flex flex-col items-center mt-16 gap-6">
<!-- 页码信息 -->
<div class="text-base-content/70 font-light">
<span class="px-4 py-2 bg-base-200/50 rounded-full">
Page <%= collection.current_page %> of <%= collection.total_pages %>
</span>
</div>
<!-- 分页控件 -->
<div class="join shadow-lg">
<!-- 首页 -->
<%= link_to url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
<% end %>
<!-- 上一页 -->
<%= link_to url_for(page: collection.prev_page || 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn #{collection.first_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<% end %>
<!-- 页码 -->
<% page_window = 2 # 当前页面前后显示的页码数 %>
<% start_page = [1, collection.current_page - page_window].max %>
<% end_page = [collection.total_pages, collection.current_page + page_window].min %>
<% if start_page > 1 %>
<%= link_to 1, url_for(page: 1, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-ghost hover:bg-primary/5" %>
<% if start_page > 2 %>
<button class="join-item btn btn-ghost btn-disabled">...</button>
<% end %>
<% end %>
<% (start_page..end_page).each do |page| %>
<% if page == collection.current_page %>
<button class="join-item btn btn-ghost bg-primary/10 font-medium">
<%= page %>
</button>
<% else %>
<%= link_to page, url_for(page: page, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-ghost hover:bg-primary/5" %>
<% end %>
<% end %>
<% if end_page < collection.total_pages %>
<% if end_page < collection.total_pages - 1 %>
<button class="join-item btn btn-ghost btn-disabled">...</button>
<% end %>
<%= link_to collection.total_pages,
url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn btn-ghost hover:bg-primary/5" %>
<% end %>
<!-- 下一页 -->
<%= link_to url_for(page: collection.next_page || collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<% end %>
<!-- 末页 -->
<%= link_to url_for(page: collection.total_pages, region: params[:region], country: params[:country], sort: params[:sort]),
class: "join-item btn #{collection.last_page? ? 'btn-disabled' : 'btn-ghost'}" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>
<% end %>
</div>
<!-- 结果统计 -->
<div class="text-sm text-base-content/60 font-light">
Showing <%= collection.offset_value + 1 %> to
<%= collection.last_page? ? collection.total_count : collection.offset_value + collection.limit_value %>
of <%= collection.total_count %> <%= collection_name || 'items' %>
</div>
</div>
<% end %>