dm: 사례 콘텐츠
This commit is contained in:
@@ -20,6 +20,7 @@ NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&ver
|
||||
NEXT_PUBLIC_GATEWAY_URL=http://con.mind-one.co.kr:11083 # 개발 서버 API
|
||||
# NEXT_PUBLIC_GATEWAY_URL=http://localhost:8883 # 개발 서버 API
|
||||
CONTENTS_INTRO = 31
|
||||
CONTENTS_EXAMPLE = 32
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
|
||||
@@ -21,6 +21,7 @@ NEXT_PUBLIC_GATEWAY_URL=http://con.mind-one.co.kr:11083 # 개발 서버 API
|
||||
# NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||
CONTENTS_INTRO = 31
|
||||
CONTENTS_EXAMPLE = 32
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
|
||||
@@ -19,6 +19,7 @@ NEXT_PUBLIC_NGII_URL = http://172.29.143.87/poiact
|
||||
NEXT_PUBLIC_NGII_PARAM = BDE2BEF39DAFEC293AB6730150F222AC&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={y}&tilerow={x}
|
||||
NEXT_PUBLIC_GATEWAY_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
CONTENTS_INTRO = 31
|
||||
CONTENTS_EXAMPLE = 32
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,191 +1,415 @@
|
||||
"use client";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle"> <span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" /> <span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" /></span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type SupplyRow = {
|
||||
facility: string;
|
||||
usage: string;
|
||||
amount: string;
|
||||
};
|
||||
|
||||
function SupplyTable({ rows }: { rows: SupplyRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 px-10">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-2 text-[18px] font-medium text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#40B5A5] px-4 py-2 text-[18px] font-medium text-white">
|
||||
이용용도
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#73BF4B] px-4 py-2 text-[18px] font-medium text-white">
|
||||
공급량(㎥)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.usage}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.amount}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
totalCost,
|
||||
treatment,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
totalCost: string;
|
||||
treatment: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-10 px-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>총사업비 : {totalCost}</li>
|
||||
<li>처리공법 : {treatment}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export default function GreywaterExampleSection() {
|
||||
const title = "중수도 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root) return;
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
const sliders = Array.from(root.querySelectorAll(".image-slider"));
|
||||
|
||||
sliders.forEach((sliderElement) => {
|
||||
const slider = sliderElement as HTMLElement;
|
||||
const viewport = slider.querySelector(
|
||||
".image-slider-viewport",
|
||||
) as HTMLElement | null;
|
||||
const track = slider.querySelector(".image-slider-track") as HTMLElement | null;
|
||||
const dotsWrap = slider.querySelector(
|
||||
".image-slider-dots",
|
||||
) as HTMLElement | null;
|
||||
const pages = Array.from(
|
||||
slider.querySelectorAll(".image-slider-page"),
|
||||
) as HTMLElement[];
|
||||
|
||||
if (!viewport || !track || !dotsWrap || pages.length === 0) return;
|
||||
|
||||
let page = 0;
|
||||
let dragging = false;
|
||||
let isHover = false;
|
||||
let startX = 0;
|
||||
let deltaX = 0;
|
||||
let timer: number | null = null;
|
||||
|
||||
const autoPlayMs = Number(slider.dataset.autoplay ?? "4500");
|
||||
const canSlide = pages.length > 1;
|
||||
|
||||
if (!canSlide) {
|
||||
dotsWrap.style.display = "none";
|
||||
} else {
|
||||
dotsWrap.style.display = "";
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
track.style.transform = `translateX(-${page * 100}%)`;
|
||||
|
||||
Array.from(dotsWrap.querySelectorAll("button")).forEach((dot, index) => {
|
||||
dot.classList.toggle("is-active", index === page);
|
||||
});
|
||||
};
|
||||
|
||||
const goTo = (nextPage: number) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
if (nextPage < 0) page = pages.length - 1;
|
||||
else if (nextPage >= pages.length) page = 0;
|
||||
else page = nextPage;
|
||||
|
||||
render();
|
||||
};
|
||||
|
||||
const stopTimer = () => {
|
||||
if (timer !== null) {
|
||||
window.clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
stopTimer();
|
||||
if (!canSlide || dragging || isHover) return;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
page = page + 1 >= pages.length ? 0 : page + 1;
|
||||
render();
|
||||
}, autoPlayMs);
|
||||
};
|
||||
|
||||
const dotButtons = pages.map((_, index) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "image-slider-dot";
|
||||
|
||||
const onClick = () => {
|
||||
goTo(index);
|
||||
startTimer();
|
||||
};
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
dotsWrap.appendChild(button);
|
||||
|
||||
cleanups.push(() => {
|
||||
button.removeEventListener("click", onClick);
|
||||
});
|
||||
|
||||
return button;
|
||||
});
|
||||
|
||||
const onMouseEnter = () => {
|
||||
isHover = true;
|
||||
stopTimer();
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
isHover = false;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
dragging = true;
|
||||
startX = event.clientX;
|
||||
deltaX = 0;
|
||||
viewport.setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
const onPointerMove = (event: PointerEvent) => {
|
||||
if (!dragging) return;
|
||||
deltaX = event.clientX - startX;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (!dragging) return;
|
||||
|
||||
dragging = false;
|
||||
|
||||
if (deltaX > 50) goTo(page - 1);
|
||||
if (deltaX < -50) goTo(page + 1);
|
||||
|
||||
deltaX = 0;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
viewport.addEventListener("mouseenter", onMouseEnter);
|
||||
viewport.addEventListener("mouseleave", onMouseLeave);
|
||||
viewport.addEventListener("pointerdown", onPointerDown);
|
||||
viewport.addEventListener("pointermove", onPointerMove);
|
||||
viewport.addEventListener("pointerup", onPointerUp);
|
||||
viewport.addEventListener("pointercancel", onPointerUp);
|
||||
|
||||
cleanups.push(() => {
|
||||
stopTimer();
|
||||
viewport.removeEventListener("mouseenter", onMouseEnter);
|
||||
viewport.removeEventListener("mouseleave", onMouseLeave);
|
||||
viewport.removeEventListener("pointerdown", onPointerDown);
|
||||
viewport.removeEventListener("pointermove", onPointerMove);
|
||||
viewport.removeEventListener("pointerup", onPointerUp);
|
||||
viewport.removeEventListener("pointercancel", onPointerUp);
|
||||
dotButtons.forEach((button) => button.remove());
|
||||
});
|
||||
|
||||
render();
|
||||
startTimer();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const contentsHtml = `
|
||||
<div class="contents-page">
|
||||
<div class="page-title">
|
||||
<h2>
|
||||
<span class="title-text">중수도 사례</span>
|
||||
<span class="title-bg"> </span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-greywater-ansan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 안산시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="안산시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="81억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
<div class="sections greywater-example-sections">
|
||||
<section id="example-greywater-ansan" class="section first">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>경기도 안산시</h3>
|
||||
</div>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "경기테크노파크", usage: "청소·화장실용수", amount: "40㎥/일" },
|
||||
{ facility: "올림픽수영장", usage: "청소·화장실/세척·살수용수", amount: "35㎥/일" },
|
||||
{ facility: "와스타디움", usage: "청소·화장실/조경용수", amount: "90㎥/일" },
|
||||
{ facility: "호수실내수영장", usage: "청소·화장실/조경/세척·살수용수", amount: "50㎥/일" },
|
||||
{ facility: "한양대학교 에리카캠퍼스", usage: "청소·화장실용수", amount: "370㎥/일" },
|
||||
]}
|
||||
/>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업명</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>안산시 중수도 설치사업</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업목적</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업 주요 내용</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 81억원</li>
|
||||
<li>처리공법 : MBLM공법</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업량</h4>
|
||||
</div>
|
||||
<div class="table-wrap greywater-table-wrap">
|
||||
<table class="contents-table greywater-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-blue">시설명</th>
|
||||
<th class="th-teal-greywater">이용용도</th>
|
||||
<th class="th-green">공급량(㎥)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>경기테크노파크</td><td>청소·화장실용수</td><td>40㎥/일</td></tr>
|
||||
<tr><td>올림픽수영장</td><td>청소·화장실/세척·살수용수</td><td>35㎥/일</td></tr>
|
||||
<tr><td>와스타디움</td><td>청소·화장실/조경용수</td><td>90㎥/일</td></tr>
|
||||
<tr><td>호수실내수영장</td><td>청소·화장실/조경/세척·살수용수</td><td>50㎥/일</td></tr>
|
||||
<tr><td>한양대학교 에리카캠퍼스</td><td>청소·화장실용수</td><td>370㎥/일</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-greywater-yongin" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 용인시</SectionTitle>
|
||||
<section id="example-greywater-yongin" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>경기도 용인시</h3>
|
||||
</div>
|
||||
|
||||
<InfoList
|
||||
projectName="용인시 중수도 설치사업"
|
||||
purpose="물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용"
|
||||
totalCost="40억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업명</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>용인시 중수도 설치사업</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "여성회관", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "수지아르피아", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "지상작전사령부", usage: "청소·화장실/조경/세척·살수용수", amount: "53㎥/일" },
|
||||
]}
|
||||
/>
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업목적</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업 주요 내용</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 40억원</li>
|
||||
<li>처리공법 : MBLM공법</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업량</h4>
|
||||
</div>
|
||||
<div class="table-wrap greywater-table-wrap">
|
||||
<table class="contents-table greywater-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-blue">시설명</th>
|
||||
<th class="th-teal-greywater">이용용도</th>
|
||||
<th class="th-green">공급량(㎥)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>여성회관</td><td>청소·화장실용수</td><td>35㎥/일</td></tr>
|
||||
<tr><td>수지아르피아</td><td>청소·화장실용수</td><td>35㎥/일</td></tr>
|
||||
<tr><td>지상작전사령부</td><td>청소·화장실/조경/세척·살수용수</td><td>53㎥/일</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-greywater-pyeongtaek" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 평택시</SectionTitle>
|
||||
<section id="example-greywater-pyeongtaek" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>경기도 평택시</h3>
|
||||
</div>
|
||||
|
||||
<InfoList
|
||||
projectName="평택시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="21억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업명</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>평택시 중수도 설치사업</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "평택대학교",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "50㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업목적</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업 주요 내용</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 21억원</li>
|
||||
<li>처리공법 : MBLM공법</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업량</h4>
|
||||
</div>
|
||||
<div class="table-wrap greywater-table-wrap">
|
||||
<table class="contents-table greywater-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-blue">시설명</th>
|
||||
<th class="th-teal-greywater">이용용도</th>
|
||||
<th class="th-green">공급량(㎥)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>평택대학교</td><td>청소·화장실용수</td><td>50㎥/일</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="bg-white"
|
||||
dangerouslySetInnerHTML={{ __html: contentsHtml }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,225 +1,382 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle">
|
||||
<span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" />
|
||||
<span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" />
|
||||
</span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
details,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
details: string[];
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-9 px-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
{details.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RainTableRow = {
|
||||
facility: string;
|
||||
storage: string;
|
||||
supply: string;
|
||||
pipe: string;
|
||||
};
|
||||
|
||||
function RainTable({ rows }: { rows: RainTableRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-3 text-[18px] font-medium text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#39B2BD] px-4 py-3 text-[18px] font-medium text-white">
|
||||
저류조용량(㎥)
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#58B99A] px-4 py-3 text-[18px] font-medium text-white">
|
||||
빗물공급기
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#70BF4B] px-4 py-3 text-[18px] font-medium text-white">
|
||||
빗물분사장치(m)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.storage}
|
||||
</td>
|
||||
|
||||
{index === 0 && (
|
||||
<td
|
||||
rowSpan={rows.length}
|
||||
className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]"
|
||||
>
|
||||
{row.supply}
|
||||
</td>
|
||||
)}
|
||||
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.pipe}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export default function RainExampleSection() {
|
||||
const title = "빗물이용시설 사례";
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root) return;
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
const sliders = Array.from(root.querySelectorAll(".image-slider"));
|
||||
|
||||
sliders.forEach((sliderElement) => {
|
||||
const slider = sliderElement as HTMLElement;
|
||||
const viewport = slider.querySelector(".image-slider-viewport") as HTMLElement | null;
|
||||
const track = slider.querySelector(".image-slider-track") as HTMLElement | null;
|
||||
const dotsWrap = slider.querySelector(".image-slider-dots") as HTMLElement | null;
|
||||
const pages = Array.from(
|
||||
slider.querySelectorAll(".image-slider-page"),
|
||||
) as HTMLElement[];
|
||||
|
||||
if (!viewport || !track || !dotsWrap || pages.length === 0) return;
|
||||
|
||||
let page = 0;
|
||||
let dragging = false;
|
||||
let isHover = false;
|
||||
let startX = 0;
|
||||
let deltaX = 0;
|
||||
let timer: number | null = null;
|
||||
|
||||
const autoPlayMs = Number(slider.dataset.autoplay ?? "4500");
|
||||
const canSlide = pages.length > 1;
|
||||
|
||||
if (!canSlide) {
|
||||
dotsWrap.style.display = "none";
|
||||
} else {
|
||||
dotsWrap.style.display = "";
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
track.style.transform = `translateX(-${page * 100}%)`;
|
||||
|
||||
Array.from(dotsWrap.querySelectorAll("button")).forEach((dot, index) => {
|
||||
dot.classList.toggle("is-active", index === page);
|
||||
});
|
||||
};
|
||||
|
||||
const goTo = (nextPage: number) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
if (nextPage < 0) page = pages.length - 1;
|
||||
else if (nextPage >= pages.length) page = 0;
|
||||
else page = nextPage;
|
||||
|
||||
render();
|
||||
};
|
||||
|
||||
const stopTimer = () => {
|
||||
if (timer !== null) {
|
||||
window.clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
stopTimer();
|
||||
if (!canSlide || dragging || isHover) return;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
page = page + 1 >= pages.length ? 0 : page + 1;
|
||||
render();
|
||||
}, autoPlayMs);
|
||||
};
|
||||
|
||||
const dotButtons = pages.map((_, index) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "image-slider-dot";
|
||||
|
||||
const onClick = () => {
|
||||
goTo(index);
|
||||
startTimer();
|
||||
};
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
dotsWrap.appendChild(button);
|
||||
|
||||
cleanups.push(() => {
|
||||
button.removeEventListener("click", onClick);
|
||||
});
|
||||
|
||||
return button;
|
||||
});
|
||||
|
||||
const onMouseEnter = () => {
|
||||
isHover = true;
|
||||
stopTimer();
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
isHover = false;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
dragging = true;
|
||||
startX = event.clientX;
|
||||
deltaX = 0;
|
||||
viewport.setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
const onPointerMove = (event: PointerEvent) => {
|
||||
if (!dragging) return;
|
||||
deltaX = event.clientX - startX;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (!dragging) return;
|
||||
|
||||
dragging = false;
|
||||
|
||||
if (deltaX > 50) goTo(page - 1);
|
||||
if (deltaX < -50) goTo(page + 1);
|
||||
|
||||
deltaX = 0;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
viewport.addEventListener("mouseenter", onMouseEnter);
|
||||
viewport.addEventListener("mouseleave", onMouseLeave);
|
||||
viewport.addEventListener("pointerdown", onPointerDown);
|
||||
viewport.addEventListener("pointermove", onPointerMove);
|
||||
viewport.addEventListener("pointerup", onPointerUp);
|
||||
viewport.addEventListener("pointercancel", onPointerUp);
|
||||
|
||||
cleanups.push(() => {
|
||||
stopTimer();
|
||||
viewport.removeEventListener("mouseenter", onMouseEnter);
|
||||
viewport.removeEventListener("mouseleave", onMouseLeave);
|
||||
viewport.removeEventListener("pointerdown", onPointerDown);
|
||||
viewport.removeEventListener("pointermove", onPointerMove);
|
||||
viewport.removeEventListener("pointerup", onPointerUp);
|
||||
viewport.removeEventListener("pointercancel", onPointerUp);
|
||||
dotButtons.forEach((button) => button.remove());
|
||||
});
|
||||
|
||||
render();
|
||||
startTimer();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const contentsHtml = `
|
||||
<div class="contents-page">
|
||||
<div class="page-title">
|
||||
<h2>
|
||||
<span class="title-text">빗물이용시설 사례</span>
|
||||
<span class="title-bg"> </span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-rain-suwon" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 수원시</SectionTitle>
|
||||
<div class="sections rain-example-sections">
|
||||
<section id="example-rain-suwon" class="section first">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>경기도 수원시</h3>
|
||||
</div>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/suwon_01.png", alt: "수원시 빗물이용시설 사례 1" },
|
||||
{ src: "/images/landing/business/rain/suwon_02.png", alt: "수원시 빗물이용시설 사례 2" },
|
||||
{ src: "/images/landing/business/rain/suwon_03.png", alt: "수원시 빗물이용시설 사례 3" },
|
||||
{ src: "/images/landing/business/rain/suwon_04.png", alt: "수원시 빗물이용시설 사례 4" },
|
||||
{ src: "/images/landing/business/rain/suwon_05.png", alt: "수원시 빗물이용시설 사례 5" },
|
||||
]}
|
||||
/>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/suwon_01.png" alt="수원시 빗물이용시설 사례 1" />
|
||||
</div>
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/suwon_02.png" alt="수원시 빗물이용시설 사례 2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/suwon_03.png" alt="수원시 빗물이용시설 사례 3" />
|
||||
</div>
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/suwon_04.png" alt="수원시 빗물이용시설 사례 4" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/suwon_05.png" alt="수원시 빗물이용시설 사례 5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfoList
|
||||
projectName="수원시 빗물이용시설 설치사업"
|
||||
purpose="월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물공급기를 설치하여 축서기 도시 열섬효과 및 도로 비산먼지 저감"
|
||||
details={[
|
||||
"총사업비 : 20억원",
|
||||
"위치 : 수원시 영통구, 팔달구 및 장안구 일원",
|
||||
]}
|
||||
/>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업명</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>수원시 빗물이용시설 설치사업</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="mt-9 px-10">
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업목적</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물공급기를 설치하여 축서기 도시 열섬효과 및 도로 비산먼지 저감</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<RainTable
|
||||
rows={[
|
||||
{
|
||||
facility: "광교중학교",
|
||||
storage: "4,500",
|
||||
supply: "1대",
|
||||
pipe: "160",
|
||||
},
|
||||
{
|
||||
facility: "수원월드컵경기장",
|
||||
storage: "12,000",
|
||||
supply: "1대",
|
||||
pipe: "451",
|
||||
},
|
||||
{
|
||||
facility: "수원종합운동장",
|
||||
storage: "10,000",
|
||||
supply: "1대",
|
||||
pipe: "231",
|
||||
},
|
||||
{
|
||||
facility: "수원컨벤션센터",
|
||||
storage: "3,600",
|
||||
supply: "1대",
|
||||
pipe: "460",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업 주요 내용</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 20억원</li>
|
||||
<li>위치 : 수원시 영통구, 팔달구 및 장안구 일원</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업량</h4>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table class="contents-table rain-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-blue">시설명</th>
|
||||
<th class="th-teal">저류조용량(㎥)</th>
|
||||
<th class="th-mint">빗물공급기</th>
|
||||
<th class="th-green">빗물분사장치(m)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>광교중학교</td>
|
||||
<td>4,500</td>
|
||||
<td rowspan="4">1대</td>
|
||||
<td>160</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>수원월드컵경기장</td>
|
||||
<td>12,000</td>
|
||||
<td>451</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>수원종합운동장</td>
|
||||
<td>10,000</td>
|
||||
<td>231</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>수원컨벤션센터</td>
|
||||
<td>3,600</td>
|
||||
<td>460</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-rain-jeju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>제주특별자치도</SectionTitle>
|
||||
<section id="example-rain-jeju" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>제주특별자치도</h3>
|
||||
</div>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/jeju_01.png", alt: "제주특별자치도 빗물이용시설 사례 1" },
|
||||
{ src: "/images/landing/business/rain/jeju_02.png", alt: "제주특별자치도 빗물이용시설 사례 2" },
|
||||
{ src: "/images/landing/business/rain/jeju_03.png", alt: "제주특별자치도 빗물이용시설 사례 3" },
|
||||
]}
|
||||
/>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/jeju_01.png" alt="제주특별자치도 빗물이용시설 사례 1" />
|
||||
</div>
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/jeju_02.png" alt="제주특별자치도 빗물이용시설 사례 2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide">
|
||||
<img src="/images/landing/business/rain/jeju_03.png" alt="제주특별자치도 빗물이용시설 사례 3" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfoList
|
||||
projectName="제주특별자치도 중규모 빗물이용시설 설치사업"
|
||||
purpose="강우시 비닐하우스에서 발생하는 대체수자원인 빗물을 순환 활용하고자 하는 사업으로, 지하수 사용 저감과 집중호우시 도로와 농경지 침수피해 예방"
|
||||
details={[
|
||||
"총사업비 : 278억원",
|
||||
"위치 : 제주특별자치도 서귀포시 남원읍 위미리 일원",
|
||||
"사업량 : 저류조 7,000㎥, 배수갑 500㎡, 관로 21.7km",
|
||||
"사업기간 : 2024. 5. ~ 2028. 12.",
|
||||
]}
|
||||
/>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업명</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>제주특별자치도 중규모 빗물이용시설 설치사업</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업목적</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>강우시 비닐하우스에서 발생하는 대체수자원인 빗물을 순환 활용하고자 하는 사업으로, 지하수 사용 저감과 집중호우시 도로와 농경지 침수피해 예방</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-block">
|
||||
<div class="block-title">
|
||||
<span class="block-title-icon">
|
||||
<span class="bubble bubble-sky"> </span>
|
||||
<span class="bubble bubble-green"> </span>
|
||||
</span>
|
||||
<h4>사업 주요 내용</h4>
|
||||
</div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 278억원</li>
|
||||
<li>위치 : 제주특별자치도 서귀포시 남원읍 위미리 일원</li>
|
||||
<li>사업량 : 저류조 7,000㎥, 배수갑 500㎡, 관로 21.7km</li>
|
||||
<li>사업기간 : 2024. 5. ~ 2028. 12.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="bg-white"
|
||||
dangerouslySetInnerHTML={{ __html: contentsHtml }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,367 +1,582 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
type SewageCase = {
|
||||
id: string;
|
||||
title: string;
|
||||
slides: {
|
||||
src: string;
|
||||
alt: string;
|
||||
}[];
|
||||
projectName: string;
|
||||
purpose: string[];
|
||||
method?: string[];
|
||||
details: string[];
|
||||
note?: string[];
|
||||
};
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle">
|
||||
<span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" />
|
||||
<span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" />
|
||||
</span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function InfoBlock({
|
||||
title,
|
||||
items,
|
||||
}: {
|
||||
title: string;
|
||||
items?: string[];
|
||||
}) {
|
||||
if (!items || items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockTitle>{title}</BlockTitle>
|
||||
<ul className="mt-4 list-disc space-y-2 pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
{items.map((item, index) => (
|
||||
<li key={`${title}-${index}`}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({ item }: { item: SewageCase }) {
|
||||
return (
|
||||
<div className="mt-10 space-y-9 px-10">
|
||||
<InfoBlock title="사업명" items={[item.projectName]} />
|
||||
<InfoBlock title="사업목적" items={item.purpose} />
|
||||
<InfoBlock title="사업방식" items={item.method} />
|
||||
<InfoBlock title="사업 주요 내용" items={item.details} />
|
||||
<InfoBlock title="특이사항" items={item.note} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sewageCases: SewageCase[] = [
|
||||
{
|
||||
id: "example-sewage-guri",
|
||||
title: "경기도 구리시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_01.png",
|
||||
alt: "구리시 도로자동청소시스템",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_02.png",
|
||||
alt: "구리시 도로자동청소시스템 위치도",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_03.png",
|
||||
alt: "구리시 도로자동청소시스템 위치도2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_04.png",
|
||||
alt: "구리시 도로자동청소시스템 원리",
|
||||
},
|
||||
],
|
||||
projectName: "구리시 도로자동청소시스템 설치사업",
|
||||
purpose: [
|
||||
"고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 61억원",
|
||||
"위치 : 구리시 경춘로 및 갈매중앙로 일대 등",
|
||||
"관로 : 5.8km",
|
||||
"공급량 : 178㎥/일",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-bucheon",
|
||||
title: "경기도 부천시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/bucheon_01.png",
|
||||
alt: "부천시 하수 재이용수 생산시설",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/bucheon_02.png",
|
||||
alt: "부천시 하수처리수 재이용 사례",
|
||||
},
|
||||
],
|
||||
projectName: "부천시 하수 재이용수 생산시설 설치 운영",
|
||||
purpose: [
|
||||
"굴포하수처리시설 하수처리수 재이용수 생산시설 설치 운영을 통해 여월천 등 부천 관내 하천의 건천화 방지, 하천 생태계 보존 및 주민들에게 친수 공간 제공",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 212억원",
|
||||
"위치 : 부천시 여월천, 삼정천, 심곡천 및 시민의 강",
|
||||
"관로 : 23.6km",
|
||||
"공급량 : 50,000㎥/일",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-suwon",
|
||||
title: "경기도 수원시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_01.png",
|
||||
alt: "수원시 황구지천1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_02.png",
|
||||
alt: "수원시 황구지천2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_03.png",
|
||||
alt: "수원시 황구지천 지도",
|
||||
},
|
||||
],
|
||||
projectName: "수원시 황구지천 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"황구지천 공공하수처리시설 하수처리수 재이용사업으로 황구 호매실천 및 금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 110억원",
|
||||
"위치 : 수원시 권선구 호매실천, 금곡천",
|
||||
"관로 : 3.9km",
|
||||
"공급량 : 25,000㎥/일(하수처리시설 용량 : 45,000㎥/일)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-paju",
|
||||
title: "경기도 파주시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_01.png",
|
||||
alt: "파주시 하수처리수 시설 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_02.png",
|
||||
alt: "파주시 하수처리수 시설 전경2",
|
||||
},
|
||||
],
|
||||
projectName: "파주시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"운정 및 금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주LCD산업단지 내 3개 업체에 공업용수를 공급(LG디스플레이, 희성전자, 엔비텍)",
|
||||
"하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)"],
|
||||
details: [
|
||||
"총사업비 : 651억원",
|
||||
"위치 : 파주시 금촌동 714번지 일원",
|
||||
"관로 : 13.6km",
|
||||
"공급량 : 40,000㎥/일(시설용량 : 41,200㎥/일)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-pohang",
|
||||
title: "경상북도 포항시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_01.png",
|
||||
alt: "포항시 하수처리수 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_02.png",
|
||||
alt: "포항시 하수처리수 전경2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_03.png",
|
||||
alt: "포항시 하수처리수 재이용 공급계통",
|
||||
},
|
||||
],
|
||||
projectName: "포항시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"포항시 내 공단 개발 확대로 지속적이고 안정적인 공업용수 공급 추진(포스코, 동국산업 등)",
|
||||
],
|
||||
method: ["민간투자사업(운영사 : ㈜포웰, 한국수자원공사)"],
|
||||
details: [
|
||||
"총사업비 : 1,400억원",
|
||||
"위치 : 포항시 남구 상도동 125번지 일원",
|
||||
"관로 : 11.7km",
|
||||
"공급량 : 100,000㎥/일(시설용량 : 100,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-ulsan",
|
||||
title: "울산광역시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_01.png",
|
||||
alt: "울산광역시 폐수처리수 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_02.png",
|
||||
alt: "울산광역시 폐수처리수 전경2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_03.png",
|
||||
alt: "울산광역시 폐수처리수 시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_04.png",
|
||||
alt: "울산광역시 폐수처리수 시설2",
|
||||
},
|
||||
],
|
||||
projectName: "울산광역시 폐수처리수 재이용사업",
|
||||
purpose: [
|
||||
"’18년 갈수기시 낙동강 수질 악화로 울산석유화학단지 용수 공급 부족을 해소하기 위해 미포산단 내 한화케미칼 등 8개사 공업용수 공급",
|
||||
],
|
||||
method: ["민간투자사업(시행사 :울산화이텍워터(주))"],
|
||||
details: [
|
||||
"총사업비 : 1,729억원",
|
||||
"위치 : 울산광역시 울주군 청량읍 상개로 14 일원",
|
||||
"관로 : 11.7km",
|
||||
"공급량 : 17,000㎥/일(시설용량 : 86,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O, EDI)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-yeosu",
|
||||
title: "전라남도 여수시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_01.png",
|
||||
alt: "여수시 하수처리수 재이용시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_02.png",
|
||||
alt: "여수시 하수처리수 재이용시설2",
|
||||
},
|
||||
],
|
||||
projectName: "여수시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"수요자가 원하는 양질의 공업용수를 저렴하게 공급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선을 위한 여수국가산단 내 공업용수 공급(GS칼텍스, YNCC 등)",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 여수그린허브(주), 운영사 : 테크로스)"],
|
||||
details: [
|
||||
"총사업비 : 750억원",
|
||||
"위치 : 여수시 신월로 284 일원",
|
||||
"관로 : 18.6km",
|
||||
"공급량 : 50,000㎥/일(시설용량 : 50,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-boryeong",
|
||||
title: "충청남도 보령시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/boryeong_01.png",
|
||||
alt: "보령시 하수처리수 재이용시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/boryeong_02.png",
|
||||
alt: "보령시 하수처리수 재이용시설2",
|
||||
},
|
||||
],
|
||||
projectName: "보령시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"한국중부발전(주)의 보령 및 신보령화력발전소에 10,000㎥/일의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진",
|
||||
"국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(기후에너지환경부, 충청남도, 보령시, 한국중부발전(주))를 바탕으로 추진되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모",
|
||||
],
|
||||
method: ["재정사업(2017년 한국환경공단과 사업대행 위·수탁협약 체결)"],
|
||||
details: [
|
||||
"총사업비 : 315억원",
|
||||
"위치 : 보령시 대천방조제로 322 일원",
|
||||
"관로 : 8.7km",
|
||||
"공급량 : 10,000㎥/일(시설용량 : 10,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
note: [
|
||||
"국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕으로 추진 되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-asan",
|
||||
title: "충청남도 아산시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/asan_01.png",
|
||||
alt: "아산시 하수처리수 재이용시설",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/asan_02.png",
|
||||
alt: "아산시 하수처리수 재이용 공정도",
|
||||
},
|
||||
],
|
||||
projectName: "아산시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"신도시 개발에 따른 인구 급증으로 발생하는 하수를 재처리하여 탕정산업단지(삼성디스플레이)에 공업용수를 안정적으로 공급함으로써 지역기반 및 활성화를 도모",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 아산스마트원터, 운영사 : 한국수자원공사)"],
|
||||
details: [
|
||||
"총사업비 : 297억원",
|
||||
"위치 : 아산시 탕정면 갈산리 807 일원",
|
||||
"관로 : 4.2km",
|
||||
"공급량 : 27,000㎥/일(시설용량 : 45,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
];
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export default function SewageExampleSection() {
|
||||
const title = "하수처리수 재이용 사례";
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root) return;
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
const sliders = Array.from(root.querySelectorAll(".image-slider"));
|
||||
|
||||
sliders.forEach((sliderElement) => {
|
||||
const slider = sliderElement as HTMLElement;
|
||||
const viewport = slider.querySelector(
|
||||
".image-slider-viewport",
|
||||
) as HTMLElement | null;
|
||||
const track = slider.querySelector(".image-slider-track") as HTMLElement | null;
|
||||
const dotsWrap = slider.querySelector(
|
||||
".image-slider-dots",
|
||||
) as HTMLElement | null;
|
||||
const pages = Array.from(
|
||||
slider.querySelectorAll(".image-slider-page"),
|
||||
) as HTMLElement[];
|
||||
|
||||
if (!viewport || !track || !dotsWrap || pages.length === 0) return;
|
||||
|
||||
let page = 0;
|
||||
let dragging = false;
|
||||
let isHover = false;
|
||||
let startX = 0;
|
||||
let deltaX = 0;
|
||||
let timer: number | null = null;
|
||||
|
||||
const autoPlayMs = Number(slider.dataset.autoplay ?? "4500");
|
||||
const canSlide = pages.length > 1;
|
||||
|
||||
if (!canSlide) {
|
||||
dotsWrap.style.display = "none";
|
||||
} else {
|
||||
dotsWrap.style.display = "";
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
track.style.transform = `translateX(-${page * 100}%)`;
|
||||
|
||||
Array.from(dotsWrap.querySelectorAll("button")).forEach((dot, index) => {
|
||||
dot.classList.toggle("is-active", index === page);
|
||||
});
|
||||
};
|
||||
|
||||
const goTo = (nextPage: number) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
if (nextPage < 0) page = pages.length - 1;
|
||||
else if (nextPage >= pages.length) page = 0;
|
||||
else page = nextPage;
|
||||
|
||||
render();
|
||||
};
|
||||
|
||||
const stopTimer = () => {
|
||||
if (timer !== null) {
|
||||
window.clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
stopTimer();
|
||||
if (!canSlide || dragging || isHover) return;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
page = page + 1 >= pages.length ? 0 : page + 1;
|
||||
render();
|
||||
}, autoPlayMs);
|
||||
};
|
||||
|
||||
const dotButtons = pages.map((_, index) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "image-slider-dot";
|
||||
|
||||
const onClick = () => {
|
||||
goTo(index);
|
||||
startTimer();
|
||||
};
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
dotsWrap.appendChild(button);
|
||||
|
||||
cleanups.push(() => {
|
||||
button.removeEventListener("click", onClick);
|
||||
});
|
||||
|
||||
return button;
|
||||
});
|
||||
|
||||
const onMouseEnter = () => {
|
||||
isHover = true;
|
||||
stopTimer();
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
isHover = false;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
dragging = true;
|
||||
startX = event.clientX;
|
||||
deltaX = 0;
|
||||
viewport.setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
const onPointerMove = (event: PointerEvent) => {
|
||||
if (!dragging) return;
|
||||
deltaX = event.clientX - startX;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (!dragging) return;
|
||||
|
||||
dragging = false;
|
||||
|
||||
if (deltaX > 50) goTo(page - 1);
|
||||
if (deltaX < -50) goTo(page + 1);
|
||||
|
||||
deltaX = 0;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
viewport.addEventListener("mouseenter", onMouseEnter);
|
||||
viewport.addEventListener("mouseleave", onMouseLeave);
|
||||
viewport.addEventListener("pointerdown", onPointerDown);
|
||||
viewport.addEventListener("pointermove", onPointerMove);
|
||||
viewport.addEventListener("pointerup", onPointerUp);
|
||||
viewport.addEventListener("pointercancel", onPointerUp);
|
||||
|
||||
cleanups.push(() => {
|
||||
stopTimer();
|
||||
viewport.removeEventListener("mouseenter", onMouseEnter);
|
||||
viewport.removeEventListener("mouseleave", onMouseLeave);
|
||||
viewport.removeEventListener("pointerdown", onPointerDown);
|
||||
viewport.removeEventListener("pointermove", onPointerMove);
|
||||
viewport.removeEventListener("pointerup", onPointerUp);
|
||||
viewport.removeEventListener("pointercancel", onPointerUp);
|
||||
dotButtons.forEach((button) => button.remove());
|
||||
});
|
||||
|
||||
render();
|
||||
startTimer();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const contentsHtml = `
|
||||
<div class="contents-page">
|
||||
<div class="page-title">
|
||||
<h2>
|
||||
<span class="title-text">하수처리수 재이용 사례</span>
|
||||
<span class="title-bg"> </span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
{sewageCases.map((item, index) => (
|
||||
<section key={item.id} id={item.id} className="scroll-mt-[120px]">
|
||||
<SectionTitle>{item.title}</SectionTitle>
|
||||
<div class="sections sewage-example-sections">
|
||||
<section id="example-sewage-guri" class="section first">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"> </span>
|
||||
<h3>경기도 구리시</h3>
|
||||
</div>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={item.slides}
|
||||
/>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/guri_01.png" alt="구리시 도로자동청소시스템" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/guri_02.png" alt="구리시 도로자동청소시스템 위치도" /></div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/guri_03.png" alt="구리시 도로자동청소시스템 위치도2" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/guri_04.png" alt="구리시 도로자동청소시스템 원리" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfoList item={item} />
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>구리시 도로자동청소시스템 설치사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 61억원</li>
|
||||
<li>위치 : 구리시 경춘로 및 갈매중앙로 일대 등</li>
|
||||
<li>관로 : 5.8km</li>
|
||||
<li>공급량 : 178㎥/일</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{index !== sewageCases.length - 1 && <Hr className="my-20" />}
|
||||
</section>
|
||||
))}
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-bucheon" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>경기도 부천시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/bucheon_01.png" alt="부천시 하수 재이용수 생산시설" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/bucheon_02.png" alt="부천시 하수처리수 재이용 사례" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>부천시 하수 재이용수 생산시설 설치 운영</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>굴포하수처리시설 하수처리수 재이용수 생산시설 설치 운영을 통해 여월천 등 부천 관내 하천의 건천화 방지, 하천 생태계 보존 및 주민들에게 친수 공간 제공</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 212억원</li>
|
||||
<li>위치 : 부천시 여월천, 삼정천, 심곡천 및 시민의 강</li>
|
||||
<li>관로 : 23.6km</li>
|
||||
<li>공급량 : 50,000㎥/일</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-suwon" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>경기도 수원시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/suwon_01.png" alt="수원시 황구지천1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/suwon_02.png" alt="수원시 황구지천2" /></div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/suwon_03.png" alt="수원시 황구지천 지도" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>수원시 황구지천 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>황구지천 공공하수처리시설 하수처리수 재이용사업으로 황구 호매실천 및 금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 110억원</li>
|
||||
<li>위치 : 수원시 권선구 호매실천, 금곡천</li>
|
||||
<li>관로 : 3.9km</li>
|
||||
<li>공급량 : 25,000㎥/일(하수처리시설 용량 : 45,000㎥/일)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-paju" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>경기도 파주시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/paju_01.png" alt="파주시 하수처리수 시설 전경1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/paju_02.png" alt="파주시 하수처리수 시설 전경2" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>파주시 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>운정 및 금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주LCD산업단지 내 3개 업체에 공업용수를 공급(LG디스플레이, 희성전자, 엔비텍)</li>
|
||||
<li>하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 651억원</li>
|
||||
<li>위치 : 파주시 금촌동 714번지 일원</li>
|
||||
<li>관로 : 13.6km</li>
|
||||
<li>공급량 : 40,000㎥/일(시설용량 : 41,200㎥/일)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-pohang" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>경상북도 포항시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/pohang_01.png" alt="포항시 하수처리수 전경1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/pohang_02.png" alt="포항시 하수처리수 전경2" /></div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/pohang_03.png" alt="포항시 하수처리수 재이용 공급계통" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>포항시 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>포항시 내 공단 개발 확대로 지속적이고 안정적인 공업용수 공급 추진(포스코, 동국산업 등)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>민간투자사업(운영사 : ㈜포웰, 한국수자원공사)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 1,400억원</li>
|
||||
<li>위치 : 포항시 남구 상도동 125번지 일원</li>
|
||||
<li>관로 : 11.7km</li>
|
||||
<li>공급량 : 100,000㎥/일(시설용량 : 100,000㎥/일)</li>
|
||||
<li>처리공법 : 전처리시설(U/F) + 주처리시설(R/O)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-ulsan" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>울산광역시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/ulsan_01.png" alt="울산광역시 폐수처리수 전경1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/ulsan_02.png" alt="울산광역시 폐수처리수 전경2" /></div>
|
||||
</div>
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/ulsan_03.png" alt="울산광역시 폐수처리수 시설1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/ulsan_04.png" alt="울산광역시 폐수처리수 시설2" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>울산광역시 폐수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>’18년 갈수기시 낙동강 수질 악화로 울산석유화학단지 용수 공급 부족을 해소하기 위해 미포산단 내 한화케미칼 등 8개사 공업용수 공급</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>민간투자사업(시행사 :울산화이텍워터(주))</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 1,729억원</li>
|
||||
<li>위치 : 울산광역시 울주군 청량읍 상개로 14 일원</li>
|
||||
<li>관로 : 11.7km</li>
|
||||
<li>공급량 : 17,000㎥/일(시설용량 : 86,000㎥/일)</li>
|
||||
<li>처리공법 : 전처리시설(U/F) + 주처리시설(R/O, EDI)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-yeosu" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>전라남도 여수시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/yeosu_01.png" alt="여수시 하수처리수 재이용시설1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/yeosu_02.png" alt="여수시 하수처리수 재이용시설2" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>여수시 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>수요자가 원하는 양질의 공업용수를 저렴하게 공급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선을 위한 여수국가산단 내 공업용수 공급(GS칼텍스, YNCC 등)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>민간투자사업(시행사 : 여수그린허브(주), 운영사 : 테크로스)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 750억원</li>
|
||||
<li>위치 : 여수시 신월로 284 일원</li>
|
||||
<li>관로 : 18.6km</li>
|
||||
<li>공급량 : 50,000㎥/일(시설용량 : 50,000㎥/일)</li>
|
||||
<li>처리공법 : 전처리시설(U/F) + 주처리시설(R/O)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-boryeong" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>충청남도 보령시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/boryeong_01.png" alt="보령시 하수처리수 재이용시설1" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/boryeong_02.png" alt="보령시 하수처리수 재이용시설2" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>보령시 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>한국중부발전(주)의 보령 및 신보령화력발전소에 10,000㎥/일의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진</li>
|
||||
<li>국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(기후에너지환경부, 충청남도, 보령시, 한국중부발전(주))를 바탕으로 추진되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>재정사업(2017년 한국환경공단과 사업대행 위·수탁협약 체결)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 315억원</li>
|
||||
<li>위치 : 보령시 대천방조제로 322 일원</li>
|
||||
<li>관로 : 8.7km</li>
|
||||
<li>공급량 : 10,000㎥/일(시설용량 : 10,000㎥/일)</li>
|
||||
<li>처리공법 : 전처리시설(U/F) + 주처리시설(R/O)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>특이사항</h4></div>
|
||||
<ul class="bullet-list"><li>국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕으로 추진 되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모</li></ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<section id="example-sewage-asan" class="section">
|
||||
<div class="section-title"><span class="section-bar"> </span><h3>충청남도 아산시</h3></div>
|
||||
<div class="image-slider" data-autoplay="4500">
|
||||
<div class="image-slider-inner">
|
||||
<div class="image-slider-viewport">
|
||||
<div class="image-slider-track">
|
||||
<div class="image-slider-page">
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/asan_01.png" alt="아산시 하수처리수 재이용시설" /></div>
|
||||
<div class="image-slide"><img src="/images/landing/business/sewage/asan_02.png" alt="아산시 하수처리수 재이용 공정도" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-slider-dots"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업명</h4></div>
|
||||
<ul class="bullet-list"><li>아산시 하수처리수 재이용사업</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업목적</h4></div>
|
||||
<ul class="bullet-list"><li>신도시 개발에 따른 인구 급증으로 발생하는 하수를 재처리하여 탕정산업단지(삼성디스플레이)에 공업용수를 안정적으로 공급함으로써 지역기반 및 활성화를 도모</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업방식</h4></div>
|
||||
<ul class="bullet-list"><li>민간투자사업(시행사 : 아산스마트원터, 운영사 : 한국수자원공사)</li></ul>
|
||||
</div>
|
||||
<div class="content-block">
|
||||
<div class="block-title"><span class="block-title-icon"><span class="bubble bubble-sky"> </span><span class="bubble bubble-green"> </span></span><h4>사업 주요 내용</h4></div>
|
||||
<ul class="bullet-list">
|
||||
<li>총사업비 : 297억원</li>
|
||||
<li>위치 : 아산시 탕정면 갈산리 807 일원</li>
|
||||
<li>관로 : 4.2km</li>
|
||||
<li>공급량 : 27,000㎥/일(시설용량 : 45,000㎥/일)</li>
|
||||
<li>처리공법 : 전처리시설(U/F) + 주처리시설(R/O)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="bg-white"
|
||||
dangerouslySetInnerHTML={{ __html: contentsHtml }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ContentsPage } from "@/components/ui/contents";
|
||||
|
||||
export default function ExampleSection() {
|
||||
const contsSn = Number(process.env.CONTENTS_EXAMPLE ?? 32);
|
||||
const menuCd = "24";
|
||||
|
||||
return <ContentsPage contsSn={contsSn} menuCd={menuCd} />;
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* 물 재이용 사례
|
||||
*/
|
||||
export const dynamic = "force-dynamic";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
|
||||
import RainExampleSection from "./_components/RainExampleSection";
|
||||
import GreywaterExampleSection from "./_components/GreywaterExampleSection";
|
||||
import SewageExampleSection from "./_components/SewageExampleSection";
|
||||
import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
import ExampleSection from "./exampleSection";
|
||||
// import RainExampleSection from "./_components/RainExampleSection";
|
||||
// import GreywaterExampleSection from "./_components/GreywaterExampleSection";
|
||||
// import SewageExampleSection from "./_components/SewageExampleSection";
|
||||
// import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
@@ -78,11 +80,12 @@ export default function ExamplePage() {
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
<RainExampleSection />
|
||||
<ExampleSection />
|
||||
{/* <RainExampleSection />
|
||||
<GreywaterExampleSection />
|
||||
<SewageExampleSection />
|
||||
<GlobalCaseSection />
|
||||
<GlobalCaseSection /> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ export default function LawIntroSection() {
|
||||
<div class="page-title">
|
||||
<h2>
|
||||
<span class="title-text">물재이용법</span>
|
||||
<span class="title-bg"></span>
|
||||
<span class="title-bg"> </span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="sections">
|
||||
<section id="law-plan" class="section first">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>법률 제·개정 및 기본계획 수립</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
@@ -25,7 +25,7 @@ export default function LawIntroSection() {
|
||||
|
||||
<section id="plan-performance" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>제2차 계획 대비 실적 현황</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
@@ -37,7 +37,7 @@ export default function LawIntroSection() {
|
||||
|
||||
<section id="domestic-change" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>우리나라의 수자원 현황 및 물이용 여건</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
@@ -49,7 +49,7 @@ export default function LawIntroSection() {
|
||||
|
||||
<section id="policy-history" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>물 재이용 정책 변화 추이</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
@@ -61,7 +61,7 @@ export default function LawIntroSection() {
|
||||
|
||||
<section id="law-relation" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>타법령과의 관계 검토</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
@@ -73,7 +73,7 @@ export default function LawIntroSection() {
|
||||
|
||||
<section id="domestic-linkage" class="section">
|
||||
<div class="section-title">
|
||||
<span class="section-bar"></span>
|
||||
<span class="section-bar"> </span>
|
||||
<h3>주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요</h3>
|
||||
</div>
|
||||
<div class="image-wrap">
|
||||
|
||||
@@ -85,3 +85,481 @@
|
||||
margin: 2.75rem 0;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.contents-page .rain-example-sections {
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
|
||||
.contents-page .image-slider {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-inner {
|
||||
max-width: 1240px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-viewport {
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
touch-action: pan-y;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-viewport:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-track {
|
||||
display: flex;
|
||||
transition: transform 0.5s ease-out;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-page {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.contents-page .image-slide {
|
||||
position: relative;
|
||||
height: 340px;
|
||||
overflow: hidden;
|
||||
border-radius: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.contents-page .image-slide img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
background: #d8dde3;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contents-page .image-slider-dot.is-active {
|
||||
background: #18bceb;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contents-page .image-slider-page {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.contents-page .image-slide {
|
||||
height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.contents-page .content-group {
|
||||
margin-top: 2.5rem;
|
||||
padding: 0 2.5rem;
|
||||
}
|
||||
|
||||
.contents-page .content-block + .content-block {
|
||||
margin-top: 2.25rem;
|
||||
}
|
||||
|
||||
.contents-page .block-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.contents-page .block-title-icon {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 27px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contents-page .block-title-icon .bubble {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.contents-page .block-title-icon .bubble-sky {
|
||||
left: 0;
|
||||
background: rgba(1, 172, 229, 0.4);
|
||||
}
|
||||
|
||||
.contents-page .block-title-icon .bubble-green {
|
||||
left: 7px;
|
||||
background: rgba(109, 187, 79, 0.4);
|
||||
}
|
||||
|
||||
.contents-page .block-title h4 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.contents-page .bullet-list {
|
||||
margin-top: 1rem;
|
||||
padding-left: 2rem;
|
||||
color: #4f4f4f;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.contents-page .table-wrap {
|
||||
margin-top: 2rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.contents-page .contents-table {
|
||||
width: 100%;
|
||||
min-width: 860px;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #d8dde3;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contents-page .contents-table th,
|
||||
.contents-page .contents-table td {
|
||||
border: 1px solid #d8dde3;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.contents-page .contents-table th {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contents-page .contents-table td {
|
||||
background: #fff;
|
||||
font-size: 16px;
|
||||
color: #4f4f4f;
|
||||
}
|
||||
|
||||
.contents-page .contents-table .th-blue {
|
||||
background: #14a9d9;
|
||||
}
|
||||
|
||||
.contents-page .contents-table .th-teal {
|
||||
background: #39b2bd;
|
||||
}
|
||||
|
||||
.contents-page .contents-table .th-mint {
|
||||
background: #58b99a;
|
||||
}
|
||||
|
||||
.contents-page .contents-table .th-green {
|
||||
background: #70bf4b;
|
||||
}
|
||||
|
||||
.contents-page .contents-table .th-teal-greywater {
|
||||
background: #40b5a5;
|
||||
}
|
||||
|
||||
.contents-page .contents-table th.th-gray,
|
||||
.contents-page .global-table th.th-gray {
|
||||
background: #bfc9d2;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.contents-page .contents-table th.th-blue,
|
||||
.contents-page .global-table th.th-blue {
|
||||
background: #14a9d9;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contents-page .global-table th.th-teal-2 {
|
||||
background: #55bda5;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contents-page .global-table th.th-green-2 {
|
||||
background: #6ab84e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contents-page .bg-cyan {
|
||||
background: #eaf7f8 !important;
|
||||
}
|
||||
|
||||
.contents-page .bg-green {
|
||||
background: #eaf8e6 !important;
|
||||
}
|
||||
|
||||
.contents-page .global-case-sections {
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
|
||||
.contents-page .greywater-example-sections {
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
|
||||
.contents-page .sewage-example-sections {
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
|
||||
.contents-page .global-content-group {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list {
|
||||
margin-top: 1rem;
|
||||
color: #4f4f4f;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list li + li {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list .depth-0 {
|
||||
margin-left: 1.5rem;
|
||||
padding-left: 0.25rem;
|
||||
list-style: disc;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list .depth-1 {
|
||||
margin-left: 2.125rem;
|
||||
list-style: none;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list .depth-1::before {
|
||||
content: "- ";
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list .depth-2 {
|
||||
margin-left: 3.25rem;
|
||||
list-style: none;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.contents-page .content-rich-list .depth-3 {
|
||||
margin-left: 3.5rem;
|
||||
list-style: none;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.contents-page .global-notice-box,
|
||||
.contents-page .global-description-box {
|
||||
max-width: 1000px;
|
||||
margin: 1.25rem auto 0;
|
||||
border-radius: 20px;
|
||||
background: #eaf7f8;
|
||||
}
|
||||
|
||||
.contents-page .global-notice-box {
|
||||
padding: 1.75rem 2rem;
|
||||
}
|
||||
|
||||
.contents-page .global-description-box {
|
||||
padding: 1.5rem 1.75rem;
|
||||
}
|
||||
|
||||
.contents-page .global-notice-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.contents-page .global-water-bullet {
|
||||
width: 27px;
|
||||
height: 33px;
|
||||
margin-top: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contents-page .global-notice-title {
|
||||
display: block;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: #007fa4;
|
||||
}
|
||||
|
||||
.contents-page .global-text-dark {
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.contents-page .global-plain-list {
|
||||
margin-top: 1.25rem;
|
||||
padding-left: 1.25rem;
|
||||
list-style: disc;
|
||||
color: #555;
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.contents-page .global-plain-list li + li {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.contents-page .global-table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.contents-page .global-overview-table-wrap {
|
||||
max-width: 1000px;
|
||||
margin: 1.25rem auto 0;
|
||||
}
|
||||
|
||||
.contents-page .global-table {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.contents-page .global-table th {
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contents-page .overview-compare-table {
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
.contents-page .overview-compare-table th {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.contents-page .overview-compare-table td {
|
||||
border: 1px solid #aeb9c2;
|
||||
padding: 1rem;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #4f4f4f;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.contents-page .overview-compare-table .country-cell {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.contents-page .country-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.contents-page .country-flag {
|
||||
width: 42px;
|
||||
height: 28px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.contents-page .reuse-rate-table {
|
||||
min-width: 960px;
|
||||
}
|
||||
|
||||
.contents-page .reuse-rate-table th {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.contents-page .reuse-rate-table td {
|
||||
border: 1px solid #aeb9c2;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 15px;
|
||||
color: #4f4f4f;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.contents-page .reuse-rate-table .cell-strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.contents-page .info-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.contents-page .info-card {
|
||||
border: 2px solid #01ace5;
|
||||
border-radius: 8px;
|
||||
background: #eaf5f5;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.contents-page .info-card strong {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.contents-page .info-card p {
|
||||
margin-top: 0.75rem;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.contents-page .global-table-padding {
|
||||
margin-top: 2rem;
|
||||
padding: 0 120px;
|
||||
}
|
||||
|
||||
.contents-page .global-price-table td {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.contents-page .global-accent {
|
||||
color: #01ace5;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.contents-page .info-card-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.contents-page .global-table-padding {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contents-page .global-notice-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.contents-page .global-notice-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.contents-page .info-card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import type { ContentsDetail } from "@/src/entities/content/model/contents-interface";
|
||||
|
||||
type ContentsPageClientProps = {
|
||||
@@ -7,9 +9,170 @@ type ContentsPageClientProps = {
|
||||
};
|
||||
|
||||
function ContentsPageClient({ initialContent }: ContentsPageClientProps) {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root) return;
|
||||
|
||||
const cleanups: Array<() => void> = [];
|
||||
const sliders = Array.from(root.querySelectorAll(".image-slider"));
|
||||
|
||||
sliders.forEach((sliderElement) => {
|
||||
const slider = sliderElement as HTMLElement;
|
||||
const viewport = slider.querySelector(
|
||||
".image-slider-viewport",
|
||||
) as HTMLElement | null;
|
||||
const track = slider.querySelector(
|
||||
".image-slider-track",
|
||||
) as HTMLElement | null;
|
||||
const dotsWrap = slider.querySelector(
|
||||
".image-slider-dots",
|
||||
) as HTMLElement | null;
|
||||
const pages = Array.from(
|
||||
slider.querySelectorAll(".image-slider-page"),
|
||||
) as HTMLElement[];
|
||||
|
||||
if (!viewport || !track || !dotsWrap || pages.length === 0) return;
|
||||
|
||||
let page = 0;
|
||||
let dragging = false;
|
||||
let isHover = false;
|
||||
let startX = 0;
|
||||
let deltaX = 0;
|
||||
let timer: number | null = null;
|
||||
|
||||
const autoPlayMs = Number(slider.dataset.autoplay ?? "4500");
|
||||
const canSlide = pages.length > 1;
|
||||
|
||||
if (!canSlide) {
|
||||
dotsWrap.style.display = "none";
|
||||
} else {
|
||||
dotsWrap.style.display = "";
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
track.style.transform = `translateX(-${page * 100}%)`;
|
||||
|
||||
Array.from(dotsWrap.querySelectorAll("button")).forEach((dot, index) => {
|
||||
dot.classList.toggle("is-active", index === page);
|
||||
});
|
||||
};
|
||||
|
||||
const goTo = (nextPage: number) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
if (nextPage < 0) page = pages.length - 1;
|
||||
else if (nextPage >= pages.length) page = 0;
|
||||
else page = nextPage;
|
||||
|
||||
render();
|
||||
};
|
||||
|
||||
const stopTimer = () => {
|
||||
if (timer !== null) {
|
||||
window.clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
stopTimer();
|
||||
if (!canSlide || dragging || isHover) return;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
page = page + 1 >= pages.length ? 0 : page + 1;
|
||||
render();
|
||||
}, autoPlayMs);
|
||||
};
|
||||
|
||||
const dotButtons = pages.map((_, index) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "image-slider-dot";
|
||||
|
||||
const onClick = () => {
|
||||
goTo(index);
|
||||
startTimer();
|
||||
};
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
dotsWrap.appendChild(button);
|
||||
|
||||
cleanups.push(() => {
|
||||
button.removeEventListener("click", onClick);
|
||||
});
|
||||
|
||||
return button;
|
||||
});
|
||||
|
||||
const onMouseEnter = () => {
|
||||
isHover = true;
|
||||
stopTimer();
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
isHover = false;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
dragging = true;
|
||||
startX = event.clientX;
|
||||
deltaX = 0;
|
||||
viewport.setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
const onPointerMove = (event: PointerEvent) => {
|
||||
if (!dragging) return;
|
||||
deltaX = event.clientX - startX;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (!dragging) return;
|
||||
|
||||
dragging = false;
|
||||
|
||||
if (deltaX > 50) goTo(page - 1);
|
||||
if (deltaX < -50) goTo(page + 1);
|
||||
|
||||
deltaX = 0;
|
||||
startTimer();
|
||||
};
|
||||
|
||||
viewport.addEventListener("mouseenter", onMouseEnter);
|
||||
viewport.addEventListener("mouseleave", onMouseLeave);
|
||||
viewport.addEventListener("pointerdown", onPointerDown);
|
||||
viewport.addEventListener("pointermove", onPointerMove);
|
||||
viewport.addEventListener("pointerup", onPointerUp);
|
||||
viewport.addEventListener("pointercancel", onPointerUp);
|
||||
|
||||
cleanups.push(() => {
|
||||
stopTimer();
|
||||
viewport.removeEventListener("mouseenter", onMouseEnter);
|
||||
viewport.removeEventListener("mouseleave", onMouseLeave);
|
||||
viewport.removeEventListener("pointerdown", onPointerDown);
|
||||
viewport.removeEventListener("pointermove", onPointerMove);
|
||||
viewport.removeEventListener("pointerup", onPointerUp);
|
||||
viewport.removeEventListener("pointercancel", onPointerUp);
|
||||
dotButtons.forEach((button) => button.remove());
|
||||
});
|
||||
|
||||
render();
|
||||
startTimer();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
};
|
||||
}, [initialContent?.contsCn]);
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="contents-page text-[16px] leading-[1.5]"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initialContent?.contsCn ?? "",
|
||||
|
||||
Reference in New Issue
Block a user