This commit is contained in:
zhangjianjun 2026-03-24 16:44:41 +08:00
parent b527ad343b
commit 22a98cd23a
22 changed files with 316 additions and 110 deletions

View File

@ -59,6 +59,7 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-activation": "^0.13.4", "react-activation": "^0.13.4",
"react-app-polyfill": "^3.0.0", "react-app-polyfill": "^3.0.0",
"react-countup": "^6.5.3",
"react-dev-utils": "^12.0.1", "react-dev-utils": "^12.0.1",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-refresh": "^0.11.0", "react-refresh": "^0.11.0",

View File

@ -252,11 +252,7 @@ const routes = createBrowserRouter([
{ {
path: "join/campus/detail/:id", path: "join/campus/detail/:id",
element: ( element: (
<KeepAlive id="join-campus-detail">
<Suspense fallback={null}>
<JoinCampusDetail /> <JoinCampusDetail />
</Suspense>
</KeepAlive>
), ),
}, },
// 其它 // 其它

View File

@ -28,7 +28,7 @@ export default function ScrollReveal({
once = true, once = true,
amount = 0.5, amount = 0.5,
delay = 0, delay = 0,
duration = 0.7, duration = 0.9,
as: Component = "div", as: Component = "div",
}: ScrollRevealProps) { }: ScrollRevealProps) {
const variant = ANIMATION_VARIANTS[preset]; const variant = ANIMATION_VARIANTS[preset];

View File

@ -1,4 +1,4 @@
import { useState } from "react"; import { useLayoutEffect, useRef, useState } from "react";
import styles from "./index.module.css"; import styles from "./index.module.css";
type TabItems = { type TabItems = {
@ -7,14 +7,60 @@ type TabItems = {
}[] }[]
export default function BottomTabs({ tabItems, activeIndex, setActiveIndex }: { tabItems: TabItems, activeIndex: number, setActiveIndex: (index: number) => void }) { export default function BottomTabs({ tabItems, activeIndex, setActiveIndex }: { tabItems: TabItems, activeIndex: number, setActiveIndex: (index: number) => void }) {
const tabsRef = useRef<HTMLDivElement>(null);
const tabItemRefs = useRef<(HTMLDivElement | null)[]>([]);
const [tabIndicator, setTabIndicator] = useState({ x: 0, width: 0 });
useLayoutEffect(() => {
if (!tabItems?.length) return;
const root = tabsRef.current;
if (!root) return;
const updateIndicator = () => {
const tab = tabItemRefs.current[activeIndex];
if (!tab) return;
const rootRect = root.getBoundingClientRect();
const tabRect = tab.getBoundingClientRect();
setTabIndicator({
x: tabRect.left - rootRect.left,
width: tabRect.width,
});
};
updateIndicator();
const ro = new ResizeObserver(updateIndicator);
ro.observe(root);
window.addEventListener("resize", updateIndicator);
return () => {
ro.disconnect();
window.removeEventListener("resize", updateIndicator);
};
}, [activeIndex, tabItems]);
return ( return (
<div ref={tabsRef} className={styles.bottomTabsSectionTabsWrap}>
<div
className={styles.bottomTabsSectionTabIndicator}
style={{
transform: `translateX(${tabIndicator.x}px)`,
width: tabIndicator.width,
opacity: tabIndicator.width > 0 ? 1 : 0,
}}
/>
<div className={styles.bottomTabsSectionContentTabs}> <div className={styles.bottomTabsSectionContentTabs}>
{tabItems.map((item, i) => ( {tabItems.map((item, i) => (
<div key={i} className={`${styles.bottomTabsSectionContentTab} ${activeIndex === i ? styles.active : ''}`} onClick={() => setActiveIndex(i)}> <div
key={i}
ref={(el) => {
tabItemRefs.current[i] = el;
}}
className={styles.bottomTabsSectionContentTab}
onClick={() => setActiveIndex(i)}
>
<span>{item.tabName}</span> <span>{item.tabName}</span>
</div> </div>
))} ))}
</div> </div>
</div>
) )
} }

View File

@ -1,8 +1,10 @@
.bottomTabsSection { .bottomTabsSection {
position: relative;
overflow: hidden;
width: 100%; width: 100%;
/* height: 1080px; */ /* height: 1080px; */
height: 100vh; height: 100vh;
padding: 100px auto; padding: 100px 0;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -10,8 +12,26 @@
box-sizing: border-box; box-sizing: border-box;
} }
.bottomTabsSectionBg {
position: absolute;
inset: 0;
z-index: -1;
}
.bottomTabsSectionBgLayer {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transition: opacity 0.5s ease;
}
/* Features Section */ /* Features Section */
.bottomTabsSectionContent { .bottomTabsSectionContent {
position: relative;
z-index: 1;
width: calc(100% - 520px); width: calc(100% - 520px);
margin: 0 auto; margin: 0 auto;
height: 500px; height: 500px;
@ -40,16 +60,30 @@
} }
.bottomTabsSectionContentTabs { .bottomTabsSectionContentTabs {
position: relative;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: space-evenly;
gap: auto; gap: auto;
border-top: 1px solid rgba(255,255,255,0.5); border-top: 1px solid rgba(255,255,255,0.5);
padding: 0 auto; padding: 0 auto;
} }
.bottomTabsSectionTabsWrap {
position: relative;
}
.bottomTabsSectionTabIndicator {
position: absolute;
top: 0;
left: 0;
height: 2px;
background-color: #fff;
transition: transform 0.3s ease, width 0.3s ease, opacity 0.3s ease;
pointer-events: none;
}
.bottomTabsSectionContentTab { .bottomTabsSectionContentTab {
flex: 1;
font-weight: 500; font-weight: 500;
font-size: 20px; font-size: 20px;
color: #FFFFFF; color: #FFFFFF;
@ -63,6 +97,3 @@
height: 60px; height: 60px;
} }
} }
.bottomTabsSectionContentTab.active span {
border-top: 2px solid #FFFFFF;
}

View File

@ -1,7 +1,7 @@
import styles from './index.module.css'; import styles from './index.module.css';
import SectionTitle from '../SectionTitle'; import SectionTitle from '../SectionTitle';
import { useState } from 'react'; import { useLayoutEffect, useRef, useState } from 'react';
import BottomTabs from './BottomTabs'; import ScrollReveal from '@/components/ScrollReveal';
type Data = { type Data = {
title: string; title: string;
@ -9,25 +9,129 @@ type Data = {
tabName: string; tabName: string;
contentTitle: string; contentTitle: string;
contentText: string; contentText: string;
/** 以 mockData 为准 */ content?: string;
backgroundImage?: string; backgroundImage?: string;
image?: string; image?: string;
}[] }[]
} }
const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)";
export default function BottomTabsSection({ data }: { data: Data }) { export default function BottomTabsSection({ data }: { data: Data }) {
const [activeIndex, setActiveIndex] = useState(0); const [activeIndex, setActiveIndex] = useState(0);
const bgImg = data.tabItems[activeIndex]?.backgroundImage ?? data.tabItems[activeIndex]?.image ?? ""; const tabsRef = useRef<HTMLDivElement>(null);
const tabItemRefs = useRef<(HTMLDivElement | null)[]>([]);
const [tabIndicator, setTabIndicator] = useState({ x: 0, width: 0 });
const firstBg = data.tabItems?.[0]?.backgroundImage ?? data.tabItems?.[0]?.image ?? "";
const [bgState, setBgState] = useState({
a: firstBg,
b: firstBg,
showA: true,
});
useLayoutEffect(() => {
if (!data.tabItems?.length) return;
const next = data.tabItems[activeIndex]?.backgroundImage ?? data.tabItems[activeIndex]?.image ?? "";
setBgState((prev) => {
const visible = prev.showA ? prev.a : prev.b;
if (next === visible) return prev;
if (!visible && next) {
return { a: next, b: next, showA: true };
}
if (prev.showA) {
return { ...prev, b: next, showA: false };
}
return { ...prev, a: next, showA: true };
});
}, [activeIndex, data.tabItems]);
useLayoutEffect(() => {
if (!data.tabItems?.length) return;
const root = tabsRef.current;
if (!root) return;
const updateIndicator = () => {
const tab = tabItemRefs.current[activeIndex];
if (!tab) return;
const rootRect = root.getBoundingClientRect();
const tabRect = tab.getBoundingClientRect();
setTabIndicator({
x: tabRect.left - rootRect.left,
width: tabRect.width,
});
};
updateIndicator();
const ro = new ResizeObserver(updateIndicator);
ro.observe(root);
window.addEventListener("resize", updateIndicator);
return () => {
ro.disconnect();
window.removeEventListener("resize", updateIndicator);
};
}, [activeIndex, data.tabItems]);
return ( return (
<div className={styles.bottomTabsSection} style={{ backgroundImage: bgImg ? `url(${bgImg})` : undefined }}> <div className={styles.bottomTabsSection}>
<div className={styles.bottomTabsSectionBg} aria-hidden>
<div
className={styles.bottomTabsSectionBgLayer}
style={{
zIndex: bgState.showA ? 2 : 1,
opacity: bgState.showA ? 1 : 0,
backgroundImage: `url(${bgState.a}), ${FALLBACK_GRADIENT}`,
}}
/>
<div
className={styles.bottomTabsSectionBgLayer}
style={{
zIndex: bgState.showA ? 1 : 2,
opacity: bgState.showA ? 0 : 1,
backgroundImage: `url(${bgState.b}), ${FALLBACK_GRADIENT}`,
}}
/>
</div>
<SectionTitle title={data.title} color="#fff" /> <SectionTitle title={data.title} color="#fff" />
<div className={styles.bottomTabsSectionContent}> <div className={styles.bottomTabsSectionContent}>
<div className={styles.bottomTabsSectionContentContent}> <div className={styles.bottomTabsSectionContentContent}>
<div className={styles.contentTitle}>{data.tabItems[activeIndex].contentTitle}</div> <ScrollReveal preset="slideUp" amount={0.2} delay={0.5}>
<div className={styles.contentText}>{data.tabItems[activeIndex].contentText}</div> <div className={styles.contentTitle}>{
data.tabItems[activeIndex].contentTitle ||
data.tabItems[activeIndex].tabName
}</div>
</ScrollReveal>
<ScrollReveal preset="slideUp" amount={0.2} delay={0.7}>
<div className={styles.contentText}>{
data.tabItems[activeIndex].contentText
|| (data.tabItems[activeIndex]?.content)
}</div>
</ScrollReveal>
</div>
<div ref={tabsRef} className={styles.bottomTabsSectionTabsWrap}>
<div
className={styles.bottomTabsSectionTabIndicator}
style={{
transform: `translateX(${tabIndicator.x}px)`,
width: tabIndicator.width,
opacity: tabIndicator.width > 0 ? 1 : 0,
}}
/>
<div className={styles.bottomTabsSectionContentTabs}>
{data.tabItems.map((item, i) => (
<div
key={i}
ref={(el) => {
tabItemRefs.current[i] = el;
}}
className={styles.bottomTabsSectionContentTab}
onClick={() => setActiveIndex(i)}
>
<span>{item.tabName}</span>
</div>
))}
</div>
</div> </div>
<BottomTabs tabItems={data.tabItems} activeIndex={activeIndex} setActiveIndex={setActiveIndex} />
</div> </div>
</div> </div>
) )

View File

@ -1,3 +1,4 @@
import ScrollReveal from "@/components/ScrollReveal";
import styles from "./index.module.css"; import styles from "./index.module.css";
type Props = { type Props = {
@ -12,6 +13,7 @@ export default function ColumnXGrids({ items }: Props) {
return ( return (
<div className={styles.columnXGrids}> <div className={styles.columnXGrids}>
{items.map((item, index) => ( {items.map((item, index) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={(index % 3) * 0.5}>
<div key={index} className={styles.columnXGridsItem}> <div key={index} className={styles.columnXGridsItem}>
<div className={styles.bg} style={{ backgroundImage: `url(${item.backgroundImage})` }} /> <div className={styles.bg} style={{ backgroundImage: `url(${item.backgroundImage})` }} />
<div className={styles.mask}></div> <div className={styles.mask}></div>
@ -20,6 +22,7 @@ export default function ColumnXGrids({ items }: Props) {
<div className={styles.columnXGridsItemContent}>{item.content}</div> <div className={styles.columnXGridsItemContent}>{item.content}</div>
</div> </div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
) )

View File

@ -1,3 +1,4 @@
import ScrollReveal from '@/components/ScrollReveal';
import styles from './index.module.css'; import styles from './index.module.css';
type Data = { type Data = {
@ -13,7 +14,8 @@ export default function HonorGrids({ data }: { data: Data }) {
<div className={styles.honorGridsTitle}>{data.title}</div> <div className={styles.honorGridsTitle}>{data.title}</div>
<div className={styles.honorGridsItems}> <div className={styles.honorGridsItems}>
{data.items.map((item) => ( {data.items.map((item, index) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.2}>
<div className={styles.honorGridsItem} key={item.title}> <div className={styles.honorGridsItem} key={item.title}>
<div className={styles.honorGridsItemBgleft} <div className={styles.honorGridsItemBgleft}
style={{ backgroundImage: "url(/images/icons/honor-left.png)" }} style={{ backgroundImage: "url(/images/icons/honor-left.png)" }}
@ -24,6 +26,7 @@ export default function HonorGrids({ data }: { data: Data }) {
style={{ backgroundImage: "url(/images/icons/honor-right.png)" }} style={{ backgroundImage: "url(/images/icons/honor-right.png)" }}
></div> ></div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</section> </section>

View File

@ -65,5 +65,6 @@
text-align: left; text-align: left;
font-style: normal; font-style: normal;
text-transform: none; text-transform: none;
white-space: pre-line;
} }
} }

View File

@ -1,5 +1,5 @@
import styles from "./StatsRow.module.css"; import styles from "./StatsRow.module.css";
import CountUp from "react-countup";
type Data = { type Data = {
num: string; num: string;
label: string; label: string;
@ -22,7 +22,24 @@ export default function StatsRow({ data, color }: Props) {
<div key={i} className={styles.statItem}> <div key={i} className={styles.statItem}>
<div> <div>
<span className={`${styles.statNum} font-num`}> <span className={`${styles.statNum} font-num`}>
{item.num.split(' ')[0] + ' '} {(() => {
const numberPart = item.num.split(" ")[0] ?? "";
const parsed = Number(numberPart.replace(/,/g, ""));
if (Number.isNaN(parsed)) return `${numberPart} `;
return (
<CountUp
end={parsed}
duration={2}
separator=","
decimals={Number.isInteger(parsed) ? 0 : 2}
preserveValue={false}
enableScrollSpy
scrollSpyOnce
/>
);
})()}
{" "}
</span> </span>
<span className={`${styles.statNumDesc}`}> <span className={`${styles.statNumDesc}`}>
{item.num.split(' ')[1]} {item.num.split(' ')[1]}

View File

@ -8,6 +8,7 @@ import Section from "@/components/layout/Section";
import TopTabs from "@/components/layout/TopTabsSection/TopTabs"; import TopTabs from "@/components/layout/TopTabsSection/TopTabs";
import SectionTitle from "@/components/layout/SectionTitle"; import SectionTitle from "@/components/layout/SectionTitle";
import ScrollReveal from "@/components/ScrollReveal"; import ScrollReveal from "@/components/ScrollReveal";
import BottomTabsSection from "@/components/layout/BottomTabsSection";
const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)"; const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)";
@ -127,61 +128,8 @@ export default function BusinessCommercialGroup() {
</div> </div>
</Section> </Section>
<section
className={styles.featuresHero} {section3Data && <BottomTabsSection data={section3Data} />}
>
<div className={styles.featuresHeroBg} aria-hidden>
<div
className={styles.featuresHeroBgLayer}
style={{
zIndex: featuresBg.showA ? 2 : 1,
opacity: featuresBg.showA ? 1 : 0,
backgroundImage: `url(${featuresBg.a}), ${FALLBACK_GRADIENT}`,
}}
/>
<div
className={styles.featuresHeroBgLayer}
style={{
zIndex: featuresBg.showA ? 1 : 2,
opacity: featuresBg.showA ? 0 : 1,
backgroundImage: `url(${featuresBg.b}), ${FALLBACK_GRADIENT}`,
}}
/>
</div>
<SectionTitle color="#fff" title={section3Data.title} subcontent={section3Data.content} />
<div className={styles.featuresHeroContent}>
<ScrollReveal preset="slideUp" >
<div className={styles.featuresHeroContentTitle}>{section3Data.tabItems[activeFeaturesTabIndex]?.tabName}</div>
</ScrollReveal>
<ScrollReveal preset="slideUp" >
<div className={styles.featuresHeroContentDesc}>{section3Data.tabItems[activeFeaturesTabIndex]?.content}</div>
</ScrollReveal>
</div>
<div ref={featuresHeroTabsRef} className={styles.featuresHeroTabs}>
<div
className={styles.featuresHeroTabIndicator}
style={{
transform: `translateX(${featuresTabIndicator.x}px)`,
width: featuresTabIndicator.width,
opacity: featuresTabIndicator.width > 0 ? 1 : 0,
}}
/>
<div className={styles.featuresHeroTabRow}>
{section3Data.tabItems.map((item: { tabName: string }, i: number) => (
<div
key={i}
ref={(el) => {
featuresTabItemRefs.current[i] = el;
}}
className={styles.featuresHeroTab}
onClick={() => setActiveFeaturesTabIndex(i)}
>
<span>{item.tabName}</span>
</div>
))}
</div>
</div>
</section>
<section <section
className={`${styles.twoColSection} ${styles.propertyServices}`} className={`${styles.twoColSection} ${styles.propertyServices}`}

View File

@ -4,6 +4,7 @@ import SectionTitle from "@/components/layout/SectionTitle";
import Section from "@/components/layout/Section"; import Section from "@/components/layout/Section";
import { useStore } from "@/store"; import { useStore } from "@/store";
import styles from "./InvestGroup.module.css"; import styles from "./InvestGroup.module.css";
import ScrollReveal from "@/components/ScrollReveal";
export default function InvestGroup() { export default function InvestGroup() {
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
@ -34,10 +35,12 @@ export default function InvestGroup() {
<div className={styles.investGroupContent}> <div className={styles.investGroupContent}>
<div className={styles.investGroupContentItems}> <div className={styles.investGroupContentItems}>
{section2Data.items?.map((item: { title: string; content: string }, index: number) => ( {section2Data.items?.map((item: { title: string; content: string }, index: number) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5}>
<div className={styles.investGroupContentItem} key={index}> <div className={styles.investGroupContentItem} key={index}>
<div className={styles.investGroupContentItemTitle}>{item.title}</div> <div className={styles.investGroupContentItemTitle}>{item.title}</div>
<div className={styles.investGroupContentItemContent}>{item.content}</div> <div className={styles.investGroupContentItemContent}>{item.content}</div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</div> </div>
@ -49,6 +52,7 @@ export default function InvestGroup() {
<SectionTitle title={section3Data.title} /> <SectionTitle title={section3Data.title} />
<div className={styles.equityInvestmentItems}> <div className={styles.equityInvestmentItems}>
{section3Data.items?.map((item: { logo: string; title: string }, index: number) => ( {section3Data.items?.map((item: { logo: string; title: string }, index: number) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5}>
<div className={styles.equityInvestmentItem} key={index}> <div className={styles.equityInvestmentItem} key={index}>
<img <img
src={item.logo} src={item.logo}
@ -57,6 +61,7 @@ export default function InvestGroup() {
/> />
<div className={styles.equityInvestmentItemTitle}>{item.title}</div> <div className={styles.equityInvestmentItemTitle}>{item.title}</div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</section> </section>
@ -70,10 +75,12 @@ export default function InvestGroup() {
className={styles.industryFoster} className={styles.industryFoster}
height="100vh" height="100vh"
> >
<ScrollReveal preset="slideUp" amount={0.2}>
<div className={styles.industryFosterMask}> <div className={styles.industryFosterMask}>
<div className={styles.industryFosterMaskTitle}>{section4Data.title}</div> <div className={styles.industryFosterMaskTitle}>{section4Data.title}</div>
<div className={styles.industryFosterMaskContent}>{section4Data.content}</div> <div className={styles.industryFosterMaskContent}>{section4Data.content}</div>
</div> </div>
</ScrollReveal>
</Section> </Section>
</div> </div>
)} )}

View File

@ -6,6 +6,7 @@ import Section from "@/components/layout/Section";
import StatsRow from "@/components/layout/StatsRow/StatsRow"; import StatsRow from "@/components/layout/StatsRow/StatsRow";
import { useStore } from "@/store"; import { useStore } from "@/store";
import styles from "./RuijingGroup.module.css"; import styles from "./RuijingGroup.module.css";
import ScrollReveal from "@/components/ScrollReveal";
export default function RuijingGroup() { export default function RuijingGroup() {
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
@ -50,7 +51,9 @@ export default function RuijingGroup() {
background="/images/bg-overview.png" background="/images/bg-overview.png"
maskBackground="rgba(2,17,48,0.5)" maskBackground="rgba(2,17,48,0.5)"
> >
<ScrollReveal preset="slideUp" amount={0.2} delay={0.5}>
<div className={styles.businessFeatureContent}>{section3Data.content}</div> <div className={styles.businessFeatureContent}>{section3Data.content}</div>
</ScrollReveal>
{section3Data.statsData && ( {section3Data.statsData && (
<StatsRow data={section3Data.statsData} color="#fff" /> <StatsRow data={section3Data.statsData} color="#fff" />
)} )}

View File

@ -40,7 +40,7 @@ export default function JoinCampus() {
const [businessPlateOptions, setBusinessPlateOptions] = useState<SelectOption[]>([]); const [businessPlateOptions, setBusinessPlateOptions] = useState<SelectOption[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [size] = useState(2 * supportLocales.length); const [size] = useState(5 * supportLocales.length);
const [total, setTotal] = useState(1000); const [total, setTotal] = useState(1000);
@ -79,9 +79,9 @@ export default function JoinCampus() {
categoryList?.filter((item: any) => item.type === 'job_area').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? []; categoryList?.filter((item: any) => item.type === 'job_area').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? [];
const businessPlateOptions: SelectOption[] = const businessPlateOptions: SelectOption[] =
categoryList?.filter((item: any) => item.type === 'job_unit').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? []; categoryList?.filter((item: any) => item.type === 'job_unit').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? [];
setJobTypeOptions(jobTypeOptions); setJobTypeOptions([{ label: '全部', value: '' }, ...jobTypeOptions]);
setBusinessAreaOptions(businessAreaOptions); setBusinessAreaOptions([{ label: '全部', value: '' }, ...businessAreaOptions]);
setBusinessPlateOptions(businessPlateOptions); setBusinessPlateOptions([{ label: '全部', value: '' }, ...businessPlateOptions]);
}, [categoryList]); }, [categoryList]);
useEffect(() => { useEffect(() => {
@ -140,7 +140,7 @@ export default function JoinCampus() {
<div key={index} className={styles.jobItemLabel}>&nbsp;&nbsp;&nbsp;{label}</div> <div key={index} className={styles.jobItemLabel}>&nbsp;&nbsp;&nbsp;{label}</div>
))} ))}
</div> </div>
<div className={styles.jobItemContent}>{item.content}</div> <div className={styles.jobItemContent}>{item.content}</div>
</div></Link> </div></Link>
))} ))}
</div> </div>

View File

@ -36,7 +36,7 @@ export default function CampusDetail() {
getJobDetail() getJobDetail()
}, []) }, [])
return ( return (
<div> <div style={{ minHeight: '100vh' }}>
{ {
localJobDetail && localJobDetail &&
<JobPage data={localJobDetail} /> <JobPage data={localJobDetail} />

View File

@ -1,6 +1,7 @@
import styles from "./Culture.module.css"; import styles from "./Culture.module.css";
import Banner, { type BannerConfig } from "@/components/Banner"; import Banner, { type BannerConfig } from "@/components/Banner";
import Section from "@/components/layout/Section"; import Section from "@/components/layout/Section";
import ScrollReveal from "@/components/ScrollReveal";
import { useStore } from "@/store"; import { useStore } from "@/store";
export default function Culture() { export default function Culture() {
@ -30,6 +31,7 @@ export default function Culture() {
> >
<div className={styles.cultureItems}> <div className={styles.cultureItems}>
{section1Data.items?.map((item: any, index: number) => ( {section1Data.items?.map((item: any, index: number) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5} key={index} className={styles.cultureItem}>
<div <div
key={index} key={index}
className={styles.cultureItem} className={styles.cultureItem}
@ -41,6 +43,7 @@ export default function Culture() {
<div className={styles.cultureItemContent}>{item.content}</div> <div className={styles.cultureItemContent}>{item.content}</div>
</div> </div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</Section> </Section>
@ -54,6 +57,7 @@ export default function Culture() {
> >
<div className={styles.valuesItems}> <div className={styles.valuesItems}>
{section2Data.items?.map((item: { title: string; content?: string; icon?: string }, index: number) => ( {section2Data.items?.map((item: { title: string; content?: string; icon?: string }, index: number) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5} key={index} className={styles.cultureItem}>
<div key={index} className={styles.valuesItem}> <div key={index} className={styles.valuesItem}>
<div <div
className={styles.valuesItemIcon} className={styles.valuesItemIcon}
@ -62,6 +66,7 @@ export default function Culture() {
<div className={styles.valuesItemTitle}>{item.title}</div> <div className={styles.valuesItemTitle}>{item.title}</div>
<div className={styles.valuesItemContent}>{item.content}</div> <div className={styles.valuesItemContent}>{item.content}</div>
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</Section> </Section>

View File

@ -1,6 +1,7 @@
import styles from "./Media.module.css"; import styles from "./Media.module.css";
import Banner, { type BannerConfig } from "@/components/Banner"; import Banner, { type BannerConfig } from "@/components/Banner";
import Section from "@/components/layout/Section"; import Section from "@/components/layout/Section";
import ScrollReveal from "@/components/ScrollReveal";
import { useStore } from "@/store"; import { useStore } from "@/store";
export default function Media() { export default function Media() {
@ -25,10 +26,10 @@ export default function Media() {
<Section background="/images/bg-overview.png" maskBackground="rgba(1,11,72,0.8)"> <Section background="/images/bg-overview.png" maskBackground="rgba(1,11,72,0.8)">
<div className={styles.mediaItems}> <div className={styles.mediaItems}>
{items.map((item: { title: string; content?: string }, index: number) => ( {items.map((item: { title: string; content?: string }, index: number) => (
<div key={index} className={styles.mediaItem}> <ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5} key={index} className={styles.mediaItem}>
<div className={styles.mediaItemTitle}>{item.title}</div> <div className={styles.mediaItemTitle}>{item.title}</div>
<div className={styles.mediaItemContent}>{item.content}</div> <div className={styles.mediaItemContent}>{item.content}</div>
</div> </ScrollReveal>
))} ))}
</div> </div>
</Section> </Section>

View File

@ -7,6 +7,7 @@ import Pagination from "@/components/Pagination";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useStore } from "@/store"; import { useStore } from "@/store";
import appApi from "@/api/app"; import appApi from "@/api/app";
import ScrollReveal from "@/components/ScrollReveal";
type NewsItem = { type NewsItem = {
id: number; id: number;
@ -90,6 +91,7 @@ export default function NewsPublic() {
<div className={styles.newList}> <div className={styles.newList}>
{localNewsList.map((item, index) => ( {localNewsList.map((item, index) => (
<ScrollReveal preset="slideUp" delay={(index % 3) * 0.5} key={index}>
<Link <Link
key={index} key={index}
to={`/news/detail/${item.id}`} to={`/news/detail/${item.id}`}
@ -121,6 +123,7 @@ export default function NewsPublic() {
<div className={styles.newItemCreateTime}>{item.createTime}</div> <div className={styles.newItemCreateTime}>{item.createTime}</div>
</div> </div>
</Link> </Link>
</ScrollReveal>
))} ))}
</div> </div>

View File

@ -22,7 +22,8 @@
/* 信息公开 */ /* 信息公开 */
.informationPublicDataContent { .informationPublicDataContent {
min-height: 500px; min-height: 450px;
overflow: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;

View File

@ -1,5 +1,5 @@
import Banner, { type BannerConfig } from "@/components/Banner"; import Banner, { type BannerConfig } from "@/components/Banner";
import { useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import ParagraphSection from "@/components/layout/ParagraphSection"; import ParagraphSection from "@/components/layout/ParagraphSection";
import Section from "@/components/layout/Section"; import Section from "@/components/layout/Section";
import AnimateTopCard from "@/components/layout/AnimateTopCard"; import AnimateTopCard from "@/components/layout/AnimateTopCard";
@ -8,6 +8,7 @@ import { useStore } from "@/store";
import styles from "./Foundation.module.css"; import styles from "./Foundation.module.css";
import TopTabsSection from "@/components/layout/TopTabsSection"; import TopTabsSection from "@/components/layout/TopTabsSection";
import appApi from "@/api/app"; import appApi from "@/api/app";
import ScrollReveal from "@/components/ScrollReveal";
export default function Foundation() { export default function Foundation() {
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
const data = appConfig?.social?.foundation; const data = appConfig?.social?.foundation;
@ -26,16 +27,16 @@ export default function Foundation() {
const localNewsList = useMemo(() => { const localNewsList = useMemo(() => {
return newsList.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]); return newsList.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]);
}, [newsList, locale]) }, [newsList, locale])
useEffect(() => { const getNewsList = useCallback(() => {
appApi.getNewsList({ page: 1, size: 1000, sort: "create_time DESC", appApi.getNewsList({ page: 1, size: 1000, sort: "create_time DESC",
category_id: String(categoryList?.find((item: any) => item.name.includes('【可持续发展】社会责任案例集'))?.id ?? ''), category_id: String(categoryList?.find((item: any) => item.name.includes('【银泰公益基金会】公益传播'))?.id ?? ''),
}).then((res:any) => { }).then((res:any) => {
setNewsList(res.data.items.map((item:any) => { setNewsList(res.data.items.map((item:any) => {
return { return {
id: item.id, id: item.id,
title: item.title, title: item.title,
content: item.content, content: item.content,
path: item.path, path: '/news/detail/' + item.id,
image: item.covers_show === 'image' ? item.covers.image[0] : '', image: item.covers_show === 'image' ? item.covers.image[0] : '',
video: item.covers_show === 'video' ? item.covers.video[0] : '', video: item.covers_show === 'video' ? item.covers.video[0] : '',
lang: item.lang, lang: item.lang,
@ -45,6 +46,27 @@ export default function Foundation() {
}); });
}, []) }, [])
const [fileList, setFileList] = useState<any[]>([]);
const localFileList = useMemo(() => {
return fileList.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]);
}, [fileList, locale])
const getFileList = useCallback(async () => {
const res = await appApi.getDocList({
page: 1,
size: 1000,
// category_id: String(categoryList?.find((item: any) => item.name.includes('社会责任报告'))?.id ?? '')
})
const items = res.data.items.filter((item:any) => item.category_name.includes("信息公开"))
console.log("------items-----", items)
setFileList(items)
}, [])
useEffect(() => {
getNewsList()
getFileList()
}, [])
return ( return (
<div> <div>
<Banner <Banner
@ -68,31 +90,36 @@ export default function Foundation() {
> >
<div className={styles.publicWelfareDataItems}> <div className={styles.publicWelfareDataItems}>
{localNewsList?.map((item: any, index: number) => ( {localNewsList?.map((item: any, index: number) => (
<div <ScrollReveal preset="slideUp" amount={0.2} delay={(index % 2) * 0.5}
key={index} key={index}
className={styles.publicWelfareDataItem} className={styles.publicWelfareDataItem}
> >
<AnimateTopCard data={item} /> <AnimateTopCard data={item} />
</div> </ScrollReveal>
))} ))}
</div> </div>
<div style={{ marginBottom: "50px" }} /> <div style={{ marginBottom: "50px" }} />
</Section> </Section>
)} )}
{section4Data && ( {section3Data && (
<Section <Section
title={section4Data.title} title={section3Data.title}
titleColor="#fff" titleColor="#fff"
background={section3Data.backgroundImage ?? "/images/bg-overview.png"} background={section3Data.backgroundImage ?? "/images/bg-overview.png"}
maskBackground="rgba(255,255,255,0.1)" maskBackground="rgba(255,255,255,0.1)"
height="760px"
> >
<div className={styles.informationPublicDataContent}> <div className={styles.informationPublicDataContent}>
<div className={styles.informationPublicDataItems}> <div className={styles.informationPublicDataItems}>
{section3Data.tabItems?.[activeIndex]?.fileItems?.map((item: any, index: number) => ( {localFileList
.filter((item:any) => item.category_name.includes(
section3Data.tabItems?.[activeIndex]?.tabName ?? ''
))
.map((item: any, index: number) => (
<div key={index} className={styles.informationPublicDataItem}> <div key={index} className={styles.informationPublicDataItem}>
<li> <li onClick={() => window.open(item.path, '_blank')}>
<span>{item.fileName}</span> <span>{item.name}</span>
</li> </li>
</div> </div>
))} ))}
@ -110,9 +137,11 @@ export default function Foundation() {
<Section title={section4Data.title} background="" maskBackground="#F7FBFF"> <Section title={section4Data.title} background="" maskBackground="#F7FBFF">
<div className={styles.partnerItems}> <div className={styles.partnerItems}>
{section4Data.items?.map((item: any, index: number) => ( {section4Data.items?.map((item: any, index: number) => (
<ScrollReveal preset="slideUp" amount={0.2} delay={index * 0.5} key={index}>
<div key={index} className={styles.partnerItem}> <div key={index} className={styles.partnerItem}>
<img src={item.logo} alt="logo" /> <img src={item.logo} alt="logo" />
</div> </div>
</ScrollReveal>
))} ))}
</div> </div>
</Section> </Section>

View File

@ -7,6 +7,7 @@ import AnimateTopCard from "@/components/layout/AnimateTopCard";
import { useStore } from "@/store"; import { useStore } from "@/store";
import styles from "./Sustainability.module.css"; import styles from "./Sustainability.module.css";
import appApi from "@/api/app"; import appApi from "@/api/app";
import ScrollReveal from "@/components/ScrollReveal";
type NewsItem = { type NewsItem = {
id: number; id: number;
@ -135,12 +136,13 @@ export default function Sustainability() {
</p> </p>
<div className={styles.socialResponsibilityCaseDataItems}> <div className={styles.socialResponsibilityCaseDataItems}>
{localNewsItems?.map((item: any, index: number) => ( {localNewsItems?.map((item: any, index: number) => (
<div <ScrollReveal preset="slideUp" amount={0.2} delay={(index % 2) * 0.5}
key={index} key={index}
className={styles.socialResponsibilityCaseDataItem} className={styles.socialResponsibilityCaseDataItem}
> >
<AnimateTopCard data={item} /> <AnimateTopCard data={item} />
</div> </ScrollReveal>
))} ))}
</div> </div>
</Section> </Section>
@ -154,7 +156,7 @@ export default function Sustainability() {
> >
<div className={styles.socialResponsibilityReportData}> <div className={styles.socialResponsibilityReportData}>
{localReportItems?.map((item: any, index: number) => ( {localReportItems?.map((item: any, index: number) => (
<div <ScrollReveal preset="slideUp" amount={0.2} delay={(index % 4) * 0.5}
key={index} key={index}
className={styles.socialResponsibilityReportItem} className={styles.socialResponsibilityReportItem}
> >
@ -168,7 +170,7 @@ export default function Sustainability() {
<div className={styles.socialResponsibilityReportItemTitle}> <div className={styles.socialResponsibilityReportItemTitle}>
{item.name} {item.name}
</div> </div>
</div> </ScrollReveal>
))} ))}
</div> </div>
<div <div

View File

@ -122,6 +122,11 @@ instance.interceptors.response.use(
} }
} }
} }
if(response.data.code === -1) {
Toast('系统发生异常', "error");
throw Error(response.data.message);
}
} }
return response.data; return response.data;
}, },