增加ISR静态生成功能
@ -1,11 +1,38 @@
|
||||
import Banner from "@/components/banner/Banner";
|
||||
import Category from "@/components/sidebar/Category";
|
||||
import Pagination1 from "@/components/common/Pagination1";
|
||||
import { getSiteSettings } from "@/utlis/siteSettings";
|
||||
import ListPageTemplate from "@/components/common/ListPageTemplate";
|
||||
import { notFound } from 'next/navigation';
|
||||
import DynamicListPage from "@/components/common/DynamicListPage";
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="flex justify-center items-center p-8">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const revalidate = 3600;
|
||||
export const dynamicParams = true;
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const res = await fetch(`${process.env.SITE_URL}/api/get-all-slugs`);
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch slugs: ${res.status}`);
|
||||
}
|
||||
const slugs = await res.json();
|
||||
if (!Array.isArray(slugs)) {
|
||||
console.error("generateStaticParams: received non-array from /api/get-all-slugs", slugs);
|
||||
return [];
|
||||
}
|
||||
return slugs.map(slug => ({
|
||||
slug: slug,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Could not generate static params:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const resolvedParams = await params;
|
||||
@ -35,20 +62,18 @@ export async function generateMetadata({ params }) {
|
||||
};
|
||||
}
|
||||
|
||||
export default async function DynamicPage({ params, searchParams }) {
|
||||
export default async function DynamicPage({ params }) {
|
||||
const resolvedParams = await params;
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const slugArr = resolvedParams.slug;
|
||||
const currentPage = Number(resolvedSearchParams?.page) || 1;
|
||||
|
||||
// Get global site settings
|
||||
const siteSettings = await getSiteSettings(process.env.SITE_URL);
|
||||
const pageSize = Number(siteSettings.page_size) || 12;
|
||||
|
||||
// 始终获取第一页的数据用于静态生成
|
||||
const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ slug_list: slugArr, page: currentPage, page_size: pageSize })
|
||||
body: JSON.stringify({ slug_list: slugArr, page: 1, page_size: pageSize })
|
||||
});
|
||||
const result = await res.json();
|
||||
const { data, error, total } = result;
|
||||
@ -60,26 +85,9 @@ export default async function DynamicPage({ params, searchParams }) {
|
||||
const bannerComponentName = 'Banner-' + (slugArr[0] || 'home');
|
||||
const categoryComponentName = 'Category-' + (slugArr[0] || 'home');
|
||||
|
||||
// Infer pagetype and name from slugArr
|
||||
const pagetype = slugArr?.[0] || '';
|
||||
const name = slugArr?.[1] || '';
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const currentPath = '/' + (Array.isArray(slugArr) ? slugArr.join('/') : '');
|
||||
const lastSlug = Array.isArray(slugArr) ? slugArr[slugArr.length - 1] : '';
|
||||
const totalPages = Math.ceil((total || 0) / pageSize);
|
||||
|
||||
// Adapt template required fields
|
||||
const listItems = data.map(item => ({
|
||||
slug: item.slug,
|
||||
title: item.title,
|
||||
image: item.image || item.cover || item.img || '',
|
||||
additional_title: item.additional_title || '',
|
||||
subtitle: item.subtitle || item.content || '',
|
||||
}));
|
||||
|
||||
// 新增:允许通过变量控制列数,默认4
|
||||
const listColumns = 4; // 可根据需要改为变量
|
||||
const listColumns = 4;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -89,14 +97,16 @@ export default async function DynamicPage({ params, searchParams }) {
|
||||
<div className="flex flex-col md:flex-row mx-[-15px] xl:mx-[-35px] lg:mx-[-20px]">
|
||||
<Category componentName={categoryComponentName} />
|
||||
<div className="flex-1 min-w-0 !px-[15px] max-w-full md:!px-[20px] lg:!px-[20px] xl:!px-[35px]">
|
||||
<ListPageTemplate items={listItems} basePath={currentPath} columns={listColumns} />
|
||||
<div className="mt-8">
|
||||
<Pagination1
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
<DynamicListPage
|
||||
initialItems={data}
|
||||
slugArr={slugArr}
|
||||
basePath={currentPath}
|
||||
columns={listColumns}
|
||||
pageSize={pageSize}
|
||||
totalItems={total}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
26
app/api/get-all-slugs/route.js
Normal file
@ -0,0 +1,26 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const JINGROW_SERVER_URL = process.env.JINGROW_SERVER_URL;
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_all_slugs`
|
||||
);
|
||||
|
||||
const slugs = response.data.message?.data;
|
||||
|
||||
if (!Array.isArray(slugs)) {
|
||||
console.error('API did not return an array of slugs:', response.data);
|
||||
return Response.json({ error: '返回的slugs格式不正确' }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json(slugs);
|
||||
} catch (error) {
|
||||
console.error('Error fetching slugs:', error.message, error?.response?.data);
|
||||
return Response.json(
|
||||
{ error: '获取slugs失败', detail: error?.response?.data || error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,24 @@
|
||||
import Banner from "@/components/banner/Banner";
|
||||
import Category from "@/components/sidebar/Category";
|
||||
import Pagination1 from "@/components/common/Pagination1";
|
||||
import { getSiteSettings } from "@/utlis/siteSettings";
|
||||
import ListPageTemplate from "@/components/common/ListPageTemplate";
|
||||
import { notFound } from 'next/navigation';
|
||||
import DynamicListPage from "@/components/common/DynamicListPage";
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const baseSlug = 'products';
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="flex justify-center items-center p-8">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const slugArr = [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])];
|
||||
const resolvedParams = await params;
|
||||
const slug = resolvedParams.slug || [];
|
||||
const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])];
|
||||
const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@ -36,15 +44,19 @@ export async function generateMetadata({ params }) {
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Page({ params, searchParams }) {
|
||||
const slugArr = [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])];
|
||||
const currentPage = Number(searchParams?.page) || 1;
|
||||
export default async function Page({ params }) {
|
||||
const resolvedParams = await params;
|
||||
const slug = resolvedParams.slug || [];
|
||||
const slugArr = [baseSlug, ...(Array.isArray(slug) ? slug : [slug])];
|
||||
|
||||
const siteSettings = await getSiteSettings(process.env.SITE_URL);
|
||||
const pageSize = Number(siteSettings.page_size) || 12;
|
||||
|
||||
// Fetch only page 1 for initial static render
|
||||
const res = await fetch(`${process.env.SITE_URL}/api/get-page-data`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ slug_list: slugArr, page: currentPage, page_size: pageSize })
|
||||
body: JSON.stringify({ slug_list: slugArr, page: 1, page_size: pageSize })
|
||||
});
|
||||
const result = await res.json();
|
||||
const { data, error, total } = result;
|
||||
@ -54,14 +66,8 @@ export default async function Page({ params, searchParams }) {
|
||||
const bannerComponentName = 'Banner-' + baseSlug;
|
||||
const categoryComponentName = 'Category-' + baseSlug;
|
||||
if (Array.isArray(data)) {
|
||||
const currentPath = '/' + [baseSlug, ...(params.slug ? (Array.isArray(params.slug) ? params.slug : [params.slug]) : [])].join('/');
|
||||
const totalPages = Math.ceil((total || 0) / pageSize);
|
||||
const listItems = data.map(item => ({
|
||||
slug: item.slug,
|
||||
title: item.title,
|
||||
image: item.image || item.cover || item.img || '',
|
||||
content: item.content || item.description || '',
|
||||
}));
|
||||
const currentPath = '/' + slugArr.join('/');
|
||||
const listColumns = 4;
|
||||
return (
|
||||
<>
|
||||
<Banner componentName={bannerComponentName} />
|
||||
@ -70,14 +76,16 @@ export default async function Page({ params, searchParams }) {
|
||||
<div className="flex flex-col md:flex-row mx-[-15px] xl:mx-[-35px] lg:mx-[-20px]">
|
||||
<Category componentName={categoryComponentName} />
|
||||
<div className="flex-1 min-w-0 !px-[15px] max-w-full md:!px-[20px] lg:!px-[20px] xl:!px-[35px]">
|
||||
<ListPageTemplate items={listItems} basePath={currentPath} />
|
||||
<div className="mt-8">
|
||||
<Pagination1
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
<DynamicListPage
|
||||
initialItems={data}
|
||||
slugArr={slugArr}
|
||||
basePath={currentPath}
|
||||
columns={listColumns}
|
||||
pageSize={pageSize}
|
||||
totalItems={total}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
77
components/common/DynamicListPage.jsx
Normal file
@ -0,0 +1,77 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import ListPageTemplate from '@/components/common/ListPageTemplate';
|
||||
import Pagination1 from '@/components/common/Pagination1';
|
||||
import axios from 'axios';
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="flex justify-center items-center p-8">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default function DynamicListPage({ initialItems, slugArr, basePath, columns, pageSize, totalItems }) {
|
||||
const [items, setItems] = useState(initialItems);
|
||||
const [total, setTotal] = useState(totalItems);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const searchParams = useSearchParams();
|
||||
const page = searchParams.get('page');
|
||||
const currentPage = Number(page) || 1;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPageData = async () => {
|
||||
if (currentPage === 1) {
|
||||
setItems(initialItems);
|
||||
setTotal(totalItems);
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await axios.post('/api/get-page-data', {
|
||||
slug_list: slugArr,
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
});
|
||||
setItems(res.data.data);
|
||||
setTotal(res.data.total);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch page data:', error);
|
||||
// Optionally, handle the error in the UI
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPageData();
|
||||
}, [currentPage, slugArr, pageSize, initialItems, totalItems]);
|
||||
|
||||
const listItems = items.map(item => ({
|
||||
...item,
|
||||
slug: item.slug,
|
||||
title: item.title,
|
||||
image: item.image || item.cover || item.img || '',
|
||||
additional_title: item.additional_title || '',
|
||||
subtitle: item.subtitle || item.content || '',
|
||||
}));
|
||||
|
||||
const totalPages = Math.ceil((total || 0) / pageSize);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<ListPageTemplate items={listItems} basePath={basePath} columns={columns} />
|
||||
)}
|
||||
<div className="mt-8">
|
||||
<Pagination1
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
basePath={basePath}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Navigation, Pagination, Grid } from "swiper/modules";
|
||||
import Link from "next/link";
|
||||
@ -24,9 +24,6 @@ export default function SwiperItems(props) {
|
||||
const rows = (data?.p1 && !isNaN(Number(data.p1))) ? Number(data.p1) : (props.rows || 1);
|
||||
const button_text = data?.button_text || props.button_text;
|
||||
|
||||
const prevRef = useRef(null);
|
||||
const nextRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchComponentData() {
|
||||
try {
|
||||
@ -192,13 +189,9 @@ export default function SwiperItems(props) {
|
||||
slidesPerGroup={columns * rows}
|
||||
grid={{ rows }}
|
||||
pagination={{ clickable: true, el: ".spdb15" }}
|
||||
navigation={{ prevEl: prevRef.current, nextEl: nextRef.current }}
|
||||
onInit={swiper => {
|
||||
// Swiper 8+ 需要手动传递 ref.current
|
||||
swiper.params.navigation.prevEl = prevRef.current;
|
||||
swiper.params.navigation.nextEl = nextRef.current;
|
||||
swiper.navigation.init();
|
||||
swiper.navigation.update();
|
||||
navigation={{
|
||||
prevEl: '.swiper-nav-prev-swiperitems',
|
||||
nextEl: '.swiper-nav-next-swiperitems'
|
||||
}}
|
||||
breakpoints={{
|
||||
0: { slidesPerView: 1, slidesPerGroup: 1, grid: { rows: 1 } },
|
||||
@ -249,8 +242,7 @@ export default function SwiperItems(props) {
|
||||
</Swiper>
|
||||
{/* 美化后的导航按钮 */}
|
||||
<button
|
||||
ref={prevRef}
|
||||
className="absolute left-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
className="swiper-nav-prev-swiperitems absolute left-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
style={{ color: '#b0b7c3' }}
|
||||
aria-label="上一页"
|
||||
>
|
||||
@ -259,8 +251,7 @@ export default function SwiperItems(props) {
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
ref={nextRef}
|
||||
className="absolute right-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
className="swiper-nav-next-swiperitems absolute right-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
style={{ color: '#b0b7c3' }}
|
||||
aria-label="下一页"
|
||||
>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
public/files/1699843604725442.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/files/1699843611321512.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/files/1704270166722004.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/files/1704270743246019.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/files/1704270756288208.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/files/1704270762694484.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/files/1704270769321643.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/files/1710486900857804.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/files/1710494249198915.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/files/1710566765423279.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/files/1710575819476429.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/files/1710576759732944.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/files/1710576969890036.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
public/files/1710577152564664.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/files/1710577216359592.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/files/1710577374139802.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/files/1710577433621321.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/files/1710577543664512.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/files/1710733111909648.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/files/1710739452158728.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/files/1710741261641604.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/files/1710742432108514.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/files/1710743375655253.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/files/1717143531519950.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/files/1717143636904772.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/files/1717144385476848.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/files/1717144484134102.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/files/1717145318983421.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/files/1717145850514382.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/files/1717145941383395.jpg
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/files/1717145966959952.jpg
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
public/files/1717574637276125.jpg
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/files/1717574656467011.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/files/1717575648120734.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/files/1717637294480917.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/files/1717637816329322.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/files/1717638179135036.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
public/files/1717641526287621.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/files/1717641859508907.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/files/1732343152434863.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/files/1732343157943173.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
public/files/1732343158629210.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/files/1732343158693230.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/files/1732344469125604.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/files/1732344469178325.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/files/1732344469533749.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/files/1732344470524903.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/files/1732345158643012.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/files/1732345333383085.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/files/1732346044959105.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/files/1732347046147477.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/files/1732347086497612.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/files/1732347086688416.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/files/1732347086989659.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/files/1732347086994637.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
public/files/1732348617250323.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/files/1732348617939505.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/files/1732348618266933.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/files/1732348618269171.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/files/1732348708631476.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
public/files/1732349424211378.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/files/1732349586520237.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/files/1732349587341394.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
public/files/1732349587835967.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
public/files/1732349587999431.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/files/about4.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/files/bg7.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/files/bg8.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
public/files/bg9.jpg
Normal file
|
After Width: | Height: | Size: 369 KiB |
BIN
public/files/bg95fddcf.jpg
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/files/test001.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |