save
This commit is contained in:
parent
4ca125387e
commit
bc67d4a21d
24
src/App.tsx
24
src/App.tsx
|
|
@ -31,6 +31,28 @@ function App() {
|
|||
}
|
||||
}, [])
|
||||
|
||||
const getCategoryList = useCallback(async () => {
|
||||
const results = await Promise.allSettled(
|
||||
['news', 'job_type', 'job_area', 'job_unit', "file"]
|
||||
.map(async (type) => {
|
||||
const res = await appApi.getCategoryList(type);
|
||||
return res.data.items.map((item:any) => {
|
||||
return {
|
||||
...item,
|
||||
type
|
||||
}
|
||||
})
|
||||
})
|
||||
).then((results) => {
|
||||
return results.map((result:any) => {
|
||||
return result.value;
|
||||
})
|
||||
})
|
||||
|
||||
const categoryList = results.flat(Infinity)
|
||||
useStore.getState().setCategoryList(categoryList);
|
||||
}, [])
|
||||
|
||||
const getAppConfig = useCallback(async () => {
|
||||
try {
|
||||
const res = await appApi.getAppConfig();
|
||||
|
|
@ -44,6 +66,8 @@ function App() {
|
|||
];
|
||||
useStore.getState().setSupportLocales(supportLocales);
|
||||
initState(config);
|
||||
|
||||
await getCategoryList()
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,11 @@ const app = {
|
|||
}
|
||||
});
|
||||
},
|
||||
getDocList() {
|
||||
getDocList(params: any) {
|
||||
return requests({
|
||||
url: "/yt/api/doc",
|
||||
method: "get",
|
||||
params: {
|
||||
page: 1,
|
||||
size: 1000,
|
||||
}
|
||||
params: params
|
||||
});
|
||||
},
|
||||
// 历程列表
|
||||
|
|
|
|||
|
|
@ -3,6 +3,17 @@
|
|||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.cardImage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
}
|
||||
.cardVideo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cardMask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
@ -20,6 +31,7 @@
|
|||
.cardTitle span {
|
||||
left: 30px;
|
||||
transform: translateX(0);
|
||||
text-align: left;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,8 +57,6 @@
|
|||
transition-delay: 0.2s;
|
||||
position: absolute;
|
||||
top: calc(100% - 170px);
|
||||
|
||||
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
|
|
@ -61,11 +71,20 @@
|
|||
padding: 0 30px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
transition: all 0.3s ease;
|
||||
transition-delay: 0.2s;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,36 @@
|
|||
import styles from './index.module.css';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useStore } from '@/store';
|
||||
type Data = {
|
||||
title: string;
|
||||
content: string;
|
||||
backgroundImage: string;
|
||||
image: string;
|
||||
video: string;
|
||||
path: string;
|
||||
moreText: string;
|
||||
moreText: 'moreText';
|
||||
}
|
||||
|
||||
export default function AnimateTopCard({ data }: { data: Data }) {
|
||||
const appConfig = useStore((s) => s.appConfig);
|
||||
const others = appConfig?.__global__?.others ?? {};
|
||||
|
||||
return (
|
||||
<div className={styles.card} style={{ backgroundImage: `url(${data.backgroundImage})` }}>
|
||||
<div className={styles.card}>
|
||||
{
|
||||
data.image ?
|
||||
<img src={data.image} alt={data.title} className={styles.cardImage} /> :
|
||||
<video src={data.video} autoPlay muted loop className={styles.cardVideo} />
|
||||
}
|
||||
<div className={styles.cardMask}></div>
|
||||
<div className={styles.cardInner}>
|
||||
<div className={styles.cardTitle}><span>{data.title}</span></div>
|
||||
<div className={styles.cardTitleUnderline}></div>
|
||||
<div className={styles.cardContent}>
|
||||
<div>{data.content}</div>
|
||||
<Link to={data.path} className={styles.cardMore}>{data.moreText}</Link>
|
||||
<div dangerouslySetInnerHTML={{__html: data.content}}></div>
|
||||
{
|
||||
data.moreText &&
|
||||
<Link to={data.path} className={styles.cardMore}>{others[data.moreText]}</Link>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,16 +23,19 @@
|
|||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
background: #fff;
|
||||
transition: height 0.7s ease-in-out;
|
||||
transition: height 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
.showDropPanel.header::before {
|
||||
height: 350%;
|
||||
height: 100%;
|
||||
transition: height 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.whiteMode.header::before {
|
||||
height: 100%;
|
||||
}
|
||||
|
|
@ -44,11 +47,25 @@
|
|||
transition-delay: none;
|
||||
}
|
||||
|
||||
.whiteMode .navLink, .showDropPanel .navLink {
|
||||
color: #222222;
|
||||
.keepNavDark.header::before {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.whiteMode, .showDropPanel {
|
||||
.whiteMode .navLink,
|
||||
.showDropPanel .navLink,
|
||||
.keepNavDark .navLink,
|
||||
.keepNavDark .langTrigger,
|
||||
.keepNavDark .searchBtn,
|
||||
.keepNavDark svg {
|
||||
color: #222222;
|
||||
/* transition: color 0.3s ease-in-out; */
|
||||
}
|
||||
.keepNavDark svg path {
|
||||
fill: #222222;
|
||||
}
|
||||
|
||||
.whiteMode,
|
||||
.showDropPanel {
|
||||
.searchBtn {
|
||||
color: #222222;
|
||||
}
|
||||
|
|
@ -89,21 +106,27 @@
|
|||
height: 1px;
|
||||
background: #FFFFFF;
|
||||
transform: scaleX(0);
|
||||
transform-origin: 90% 0; /* 交点:左侧 85%,右侧 15%,从此点向左右展开 */
|
||||
transform-origin: 90% 0;
|
||||
/* 交点:左侧 85%,右侧 15%,从此点向左右展开 */
|
||||
transition: transform 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
.animate {
|
||||
&.headerInner::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.crossYline {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.whiteMode, .showDropPanel {
|
||||
|
||||
.whiteMode,
|
||||
.showDropPanel {
|
||||
.headerInner::after {
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
.crossYline {
|
||||
height: 0%;
|
||||
}
|
||||
|
|
@ -116,6 +139,12 @@
|
|||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.commonMode {
|
||||
.logo img {
|
||||
transition: filter 0.5s steps(1);
|
||||
}
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -152,6 +181,7 @@
|
|||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
|
@ -164,8 +194,10 @@
|
|||
width: 1px;
|
||||
height: 0%;
|
||||
background: #FFFFFF;
|
||||
align-self: flex-end; /* 交点在最底部,从此点向上展开 */
|
||||
transform-origin: center bottom; /* 缩放从底部中心点展开 */
|
||||
align-self: flex-end;
|
||||
/* 交点在最底部,从此点向上展开 */
|
||||
transform-origin: center bottom;
|
||||
/* 缩放从底部中心点展开 */
|
||||
transition: height 1s ease-in-out;
|
||||
}
|
||||
|
||||
|
|
@ -197,16 +229,18 @@
|
|||
left: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
/* background: rgba(255, 255, 255, 0.9); */
|
||||
/* box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); */
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 0;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
transition: height 0.5s ease-in-out, padding-top 0.5s ease-in-out;
|
||||
/* transition-delay: 0.45s; */
|
||||
|
||||
&.visible {
|
||||
height: 23.75rem;
|
||||
padding-top: 1.25rem;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ export default function Header() {
|
|||
|
||||
const [activeNav, setActiveNav] = useState("");
|
||||
const [showDropPanel, setShowDropPanel] = useState(false);
|
||||
const [keepNavDark, setKeepNavDark] = useState(false);
|
||||
const leaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const [hoverElLeft, setHoverElLeft] = useState(0);
|
||||
const handleNavEnter = (e: any, path: string) => {
|
||||
const left = e.target.offsetLeft;
|
||||
|
|
@ -29,6 +31,11 @@ export default function Header() {
|
|||
setHoverElLeft(left + width / 2);
|
||||
setActiveNav(path);
|
||||
setShowDropPanel(true);
|
||||
setKeepNavDark(true);
|
||||
if (leaveTimerRef.current) {
|
||||
clearTimeout(leaveTimerRef.current);
|
||||
leaveTimerRef.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
const activePanelItem = useMemo(() => {
|
||||
|
|
@ -65,12 +72,33 @@ export default function Header() {
|
|||
}, 500);
|
||||
}, [])
|
||||
|
||||
const handleHeaderMouseLeave = () => {
|
||||
setShowDropPanel(false);
|
||||
if (leaveTimerRef.current) {
|
||||
clearTimeout(leaveTimerRef.current);
|
||||
}
|
||||
leaveTimerRef.current = setTimeout(() => {
|
||||
setKeepNavDark(false);
|
||||
leaveTimerRef.current = null;
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleHeaderMouseEnter = () => {
|
||||
if (leaveTimerRef.current) {
|
||||
clearTimeout(leaveTimerRef.current);
|
||||
leaveTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header className={`${styles.header}
|
||||
${showWhiteMode && styles.whiteMode}
|
||||
${showDropPanel && styles.showDropPanel}
|
||||
${showWhiteMode ? styles.whiteMode : ''}
|
||||
${showDropPanel ? styles.showDropPanel : ''}
|
||||
${keepNavDark ? styles.keepNavDark : ''}
|
||||
${!showWhiteMode && !showDropPanel ? styles.commonMode : ''}
|
||||
`}
|
||||
onMouseLeave={() => setShowDropPanel(false)}
|
||||
onMouseLeave={handleHeaderMouseLeave}
|
||||
onMouseEnter={handleHeaderMouseEnter}
|
||||
>
|
||||
<div className={`header-row ${styles.headerInner} ${animateHeader ? styles.animate : ""}`}>
|
||||
<Link to="/" className={styles.logo}>
|
||||
|
|
@ -117,7 +145,12 @@ export default function Header() {
|
|||
</div>
|
||||
|
||||
<DropPanel items={activePanelItem} left={hoverElLeft}
|
||||
onLinkClick={() => setShowDropPanel(false)}
|
||||
onLinkClick={() => {
|
||||
setShowDropPanel(false)
|
||||
setTimeout(() => {
|
||||
setKeepNavDark(false);
|
||||
}, 500);
|
||||
}}
|
||||
show={showDropPanel && activePanelItem.length > 0}
|
||||
/>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export default function Home() {
|
|||
|
||||
function News() {
|
||||
const locale = useStore((s) => s.locale);
|
||||
const categoryList = useStore((s) => s.categoryList);
|
||||
const [newsData, setNewsData] = useState<any[]>([]);
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
const localNewsData = useMemo(() => {
|
||||
|
|
@ -122,16 +123,10 @@ function News() {
|
|||
setNewsData(data);
|
||||
});
|
||||
}, []);
|
||||
const getCategoryList = useCallback(async () => {
|
||||
const res = await appApi.getCategoryList('news');
|
||||
const category_id = res.data.items.find((item: any) => item.name.includes('新闻资讯'))?.id;
|
||||
return category_id;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getCategoryList().then((category_id) => {
|
||||
const category_id = categoryList?.find((item: any) => item.name === '【首页】新闻资讯')?.id;
|
||||
handleSearch(category_id)
|
||||
});
|
||||
}, [])
|
||||
return (
|
||||
<Section title="新闻资讯" maskBackground="#F0F2F4">
|
||||
|
|
|
|||
|
|
@ -16,11 +16,14 @@ type JobItem = {
|
|||
content: string;
|
||||
labels: string[];
|
||||
lang: string
|
||||
}
|
||||
};
|
||||
|
||||
type SelectOption = { label: string; value: string };
|
||||
|
||||
export default function JoinCampus() {
|
||||
const appConfig = useStore((s) => s.appConfig);
|
||||
const supportLocales = useStore((s) => s.supportLocales);
|
||||
const categoryList = useStore((s) => s.categoryList);
|
||||
const locale = useStore((s) => s.locale);
|
||||
const data = appConfig?.join?.campus;
|
||||
const banner = data?.banner;
|
||||
|
|
@ -29,11 +32,11 @@ export default function JoinCampus() {
|
|||
|
||||
// 职业类别 业务领域 所属板块
|
||||
const [jobType, setJobType] = useState('');
|
||||
const [jobTypeOptions, setJobTypeOptions] = useState([]);
|
||||
const [jobTypeOptions, setJobTypeOptions] = useState<SelectOption[]>([]);
|
||||
const [businessArea, setBusinessArea] = useState('');
|
||||
const [businessAreaOptions, setBusinessAreaOptions] = useState([]);
|
||||
const [businessAreaOptions, setBusinessAreaOptions] = useState<SelectOption[]>([]);
|
||||
const [businessPlate, setBusinessPlate] = useState('');
|
||||
const [businessPlateOptions, setBusinessPlateOptions] = useState([]);
|
||||
const [businessPlateOptions, setBusinessPlateOptions] = useState<SelectOption[]>([]);
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [size] = useState(2 * supportLocales.length);
|
||||
|
|
@ -69,20 +72,16 @@ export default function JoinCampus() {
|
|||
}, 500), []);
|
||||
|
||||
const getTypes = useCallback(() => {
|
||||
['job_type', 'job_area', 'job_unit'].forEach(type => {
|
||||
appApi.getCategoryList(type).then((res) => {
|
||||
const items = res.data.items.map((item:any) => ({ label: item.name, value: item.id }));
|
||||
items.unshift({ label: "全部", value: "" });
|
||||
if (type === 'job_type') {
|
||||
setJobTypeOptions(items);
|
||||
} else if (type === 'job_area') {
|
||||
setBusinessAreaOptions(items);
|
||||
} else if (type === 'job_unit') {
|
||||
setBusinessPlateOptions(items);
|
||||
}
|
||||
})
|
||||
})
|
||||
}, []);
|
||||
const jobTypeOptions: SelectOption[] =
|
||||
categoryList?.filter((item: any) => item.type === 'job_type').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? [];
|
||||
const businessAreaOptions: SelectOption[] =
|
||||
categoryList?.filter((item: any) => item.type === 'job_area').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? [];
|
||||
const businessPlateOptions: SelectOption[] =
|
||||
categoryList?.filter((item: any) => item.type === 'job_unit').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? [];
|
||||
setJobTypeOptions(jobTypeOptions);
|
||||
setBusinessAreaOptions(businessAreaOptions);
|
||||
setBusinessPlateOptions(businessPlateOptions);
|
||||
}, [categoryList]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshData();
|
||||
|
|
|
|||
|
|
@ -21,19 +21,20 @@ export default function NewsPublic() {
|
|||
const appConfig = useStore((s) => s.appConfig);
|
||||
const locale = useStore((s) => s.locale);
|
||||
const supportLocales = useStore((s) => s.supportLocales);
|
||||
const categoryList = useStore((s) => s.categoryList);
|
||||
const data = appConfig?.news?.public;
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [size] = useState(9 * supportLocales.length);
|
||||
const [total, setTotal] = useState(0);
|
||||
const categoryIdRef = useRef<string | null>('');
|
||||
const categoryId = String(categoryList?.find((item: any) => item.name.includes('集团发布'))?.id ?? '');
|
||||
|
||||
const [newList, setNewList] = useState<NewsItem[]>([]);
|
||||
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const handleSearch = useCallback(() => {
|
||||
appApi.getNewsList({ page, size, sort: "create_time DESC", title: searchValue,
|
||||
category_id: categoryIdRef.current ?? '',
|
||||
category_id: categoryId,
|
||||
}).then((res) => {
|
||||
const data = res.data.items.map((item:any) => {
|
||||
return {
|
||||
|
|
@ -54,19 +55,10 @@ export default function NewsPublic() {
|
|||
return newList.filter(item => item.lang.toLowerCase() === locale.split('-')[0]);
|
||||
}, [newList, locale]);
|
||||
|
||||
const getCategoryList = useCallback(async () => {
|
||||
const res = await appApi.getCategoryList('news');
|
||||
const category_id = res.data.items.find((item: any) => item.name.includes('集团发布'))?.id;
|
||||
categoryIdRef.current = category_id;
|
||||
return category_id;
|
||||
}, []);
|
||||
|
||||
const banner = data?.banner;
|
||||
|
||||
useEffect(() => {
|
||||
getCategoryList().then(() => {
|
||||
handleSearch();
|
||||
})
|
||||
}, [page, size]);
|
||||
|
||||
if (!data) return null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Banner, { type BannerConfig } from "@/components/Banner";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import ParagraphSection from "@/components/layout/ParagraphSection";
|
||||
import Section from "@/components/layout/Section";
|
||||
import AnimateTopCard from "@/components/layout/AnimateTopCard";
|
||||
|
|
@ -7,20 +7,44 @@ import BottomTabs from "@/components/layout/BottomTabsSection/BottomTabs";
|
|||
import { useStore } from "@/store";
|
||||
import styles from "./Foundation.module.css";
|
||||
import TopTabsSection from "@/components/layout/TopTabsSection";
|
||||
|
||||
import appApi from "@/api/app";
|
||||
export default function Foundation() {
|
||||
const appConfig = useStore((s) => s.appConfig);
|
||||
const data = appConfig?.social?.foundation;
|
||||
const locale = useStore((s) => s.locale)
|
||||
const categoryList = useStore((s) => s.categoryList)
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const banner = data.banner;
|
||||
const section1Data = data.section1Data;
|
||||
const section2Data = data.section2Data;
|
||||
const section3Data = data.section3Data;
|
||||
const section4Data = data.section4Data;
|
||||
|
||||
|
||||
const [newsList, setNewsList] = useState<any[]>([]);
|
||||
const localNewsList = useMemo(() => {
|
||||
return newsList.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]);
|
||||
}, [newsList, locale])
|
||||
useEffect(() => {
|
||||
appApi.getNewsList({ page: 1, size: 1000, sort: "create_time DESC",
|
||||
category_id: String(categoryList?.find((item: any) => item.name.includes('【可持续发展】社会责任案例集'))?.id ?? ''),
|
||||
}).then((res:any) => {
|
||||
setNewsList(res.data.items.map((item:any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
content: item.content,
|
||||
path: item.path,
|
||||
image: item.covers_show === 'image' ? item.covers.image[0] : '',
|
||||
video: item.covers_show === 'video' ? item.covers.video[0] : '',
|
||||
lang: item.lang,
|
||||
moreText: 'moreText',
|
||||
}
|
||||
}));
|
||||
});
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Banner
|
||||
|
|
@ -43,13 +67,12 @@ export default function Foundation() {
|
|||
maskBackground="rgba(255,255,255,0.3)"
|
||||
>
|
||||
<div className={styles.publicWelfareDataItems}>
|
||||
{section2Data.items?.map((item: { title: string; content?: string; backgroundImage?: string; path?: string; moreText?: string }, index: number) => (
|
||||
{localNewsList?.map((item: any, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className={styles.publicWelfareDataItem}
|
||||
style={{ backgroundImage: `url(${item.backgroundImage})` }}
|
||||
>
|
||||
<AnimateTopCard data={{ ...item, content: item.content ?? "", path: item.path ?? "", moreText: item.moreText ?? "", backgroundImage: item.backgroundImage ?? "" }} />
|
||||
<AnimateTopCard data={item} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@
|
|||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.socialResponsibilityReportItemTitle {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,30 @@
|
|||
import Banner, { type BannerConfig } from "@/components/Banner";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import ParagraphSection from "@/components/layout/ParagraphSection";
|
||||
import ColumnXGrids from "@/components/layout/ColumnXGrids";
|
||||
import Section from "@/components/layout/Section";
|
||||
import AnimateTopCard from "@/components/layout/AnimateTopCard";
|
||||
import { useStore } from "@/store";
|
||||
import styles from "./Sustainability.module.css";
|
||||
import appApi from "@/api/app";
|
||||
|
||||
type NewsItem = {
|
||||
id: number;
|
||||
title: string;
|
||||
createTime: string;
|
||||
image: string;
|
||||
video: string;
|
||||
lang: "ZH" | "EN"
|
||||
}
|
||||
|
||||
const LANG_LEN = 2
|
||||
|
||||
export default function Sustainability() {
|
||||
const appConfig = useStore((s) => s.appConfig);
|
||||
const categoryList = useStore((s) => s.categoryList);
|
||||
const locale = useStore((s) => s.locale);
|
||||
const data = appConfig?.social?.sustainability;
|
||||
const [sliceIndex, setSliceIndex] = useState(4);
|
||||
|
||||
if (!data) return null;
|
||||
const others = appConfig?.__global__?.others ?? {};
|
||||
|
||||
const banner = data.banner;
|
||||
const section1Data = data.section1Data;
|
||||
|
|
@ -22,6 +34,81 @@ export default function Sustainability() {
|
|||
|
||||
const columnXGridsData = section2Data;
|
||||
|
||||
|
||||
// 社会责任案例新闻数据
|
||||
const [newsItems, setNewsItems] = useState<NewsItem[]>([]);
|
||||
const localNewsItems = useMemo(() => {
|
||||
return newsItems.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]);
|
||||
}, [newsItems, locale])
|
||||
|
||||
const getNewsList = useCallback(() => {
|
||||
appApi.getNewsList({
|
||||
page: 1,
|
||||
size: 8,
|
||||
sort: "create_time DESC",
|
||||
category_id: String(categoryList?.find((item: any) => item.name.includes('【可持续发展】社会责任案例集'))?.id ?? ''),
|
||||
}).then((res) => {
|
||||
const items = res.data.items.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
content: item.content,
|
||||
createTime: item.create_time,
|
||||
image: item.covers_show === 'image' ? item.covers.image[0] : '',
|
||||
video: item.covers_show === 'video' ? item.covers.video[0] : '',
|
||||
path: `/news/detail/${item.id}`,
|
||||
lang: item.lang,
|
||||
moreText: 'moreText',
|
||||
}
|
||||
})
|
||||
setNewsItems(items as NewsItem[])
|
||||
});
|
||||
}, [])
|
||||
|
||||
|
||||
// 社会责任报告
|
||||
type ReportItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
cover: string;
|
||||
}
|
||||
const [reportItems, setReportItems] = useState<ReportItem[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [size, setSize] = useState(8);
|
||||
const [total, setTotal] = useState(0);
|
||||
const localReportItems = useMemo(() => {
|
||||
return reportItems.filter((item: any) => item.lang.toLowerCase() === locale.split('-')[0]);
|
||||
}, [reportItems, locale])
|
||||
const getReportList = useCallback(async () => {
|
||||
const res = await appApi.getDocList({
|
||||
page,
|
||||
size,
|
||||
category_id: String(categoryList?.find((item: any) => item.name.includes('社会责任报告'))?.id ?? '')
|
||||
})
|
||||
setTotal(res.data.total / LANG_LEN)
|
||||
setReportItems((prev) => {
|
||||
let items = [...prev, ...res.data.items]
|
||||
const resItems: any[] = []
|
||||
// 去重 id lang 相同
|
||||
items.forEach((item: any) => {
|
||||
if(!resItems.some((i: any) => i.id === item.id && i.lang === item.lang)) {
|
||||
resItems.push(item)
|
||||
}
|
||||
})
|
||||
console.log('---resItems', resItems)
|
||||
return resItems
|
||||
})
|
||||
}, [page, size])
|
||||
|
||||
useEffect(() => {
|
||||
getNewsList()
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getReportList()
|
||||
}, [page])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Banner
|
||||
|
|
@ -47,11 +134,10 @@ export default function Sustainability() {
|
|||
{section3Data.content}
|
||||
</p>
|
||||
<div className={styles.socialResponsibilityCaseDataItems}>
|
||||
{section3Data.items?.map((item: any, index: number) => (
|
||||
{localNewsItems?.map((item: any, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className={styles.socialResponsibilityCaseDataItem}
|
||||
style={{ backgroundImage: `url(${item.backgroundImage})` }}
|
||||
>
|
||||
<AnimateTopCard data={item} />
|
||||
</div>
|
||||
|
|
@ -67,32 +153,36 @@ export default function Sustainability() {
|
|||
maskBackground="#F7FBFF"
|
||||
>
|
||||
<div className={styles.socialResponsibilityReportData}>
|
||||
{section4Data.items?.slice(0, sliceIndex).map((item: any, index: number) => (
|
||||
{localReportItems?.map((item: any, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className={styles.socialResponsibilityReportItem}
|
||||
>
|
||||
<div
|
||||
className={styles.socialResponsibilityReportItemCover}
|
||||
style={{ backgroundImage: `url(${item.coverImage})` }}
|
||||
style={{ backgroundImage: `url(${item.cover})` }}
|
||||
onClick={() => {
|
||||
window.open(item.path, '_blank')
|
||||
}}
|
||||
/>
|
||||
<div className={styles.socialResponsibilityReportItemTitle}>
|
||||
{item.title}
|
||||
{item.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className={styles.socialResponsibilityReportItemMore}
|
||||
onClick={() =>
|
||||
setSliceIndex(
|
||||
sliceIndex < (section4Data.items?.length ?? 0)
|
||||
? sliceIndex + 4
|
||||
: 4
|
||||
)
|
||||
onClick={() => {
|
||||
if(localReportItems.length < total) {
|
||||
setPage(page + 1)
|
||||
} else {
|
||||
setReportItems(prev => [...prev.slice(0, 8)])
|
||||
setPage(1)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{sliceIndex < (section4Data.items?.length ?? 0) ? "了解更多" : "收起"}
|
||||
{localReportItems.length < total ? "了解更多" : "收起"}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,16 @@ interface StoreState {
|
|||
locale: LocaleKey;
|
||||
i18nData: I18nData | null;
|
||||
appConfig: AppConfig | null;
|
||||
categoryList: {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
}[] | null;
|
||||
supportLocales: SupportLocale[];
|
||||
token: string | null;
|
||||
setLocale: (locale: LocaleKey) => void;
|
||||
setAppConfig: (data: I18nData) => void;
|
||||
setCategoryList: (list: any[]) => void;
|
||||
setToken: (token: string | null) => void;
|
||||
setSupportLocales: (locales: SupportLocale[]) => void;
|
||||
}
|
||||
|
|
@ -29,6 +35,7 @@ export const useStore = create<StoreState>()(
|
|||
locale: 'zh-CN',
|
||||
i18nData: null,
|
||||
appConfig: null,
|
||||
categoryList: null,
|
||||
supportLocales: [],
|
||||
token: null,
|
||||
setLocale: (locale) =>
|
||||
|
|
@ -41,6 +48,7 @@ export const useStore = create<StoreState>()(
|
|||
i18nData: data,
|
||||
appConfig: data[state.locale] ?? data['en-US'] ?? data['zh-CN'] ?? null,
|
||||
})),
|
||||
setCategoryList: (list) => set({ categoryList: list }),
|
||||
setToken: (token) => set({ token }),
|
||||
setSupportLocales: (locales: SupportLocale[]) => set({ supportLocales: locales }),
|
||||
}),
|
||||
|
|
@ -62,6 +70,7 @@ export const useStore = create<StoreState>()(
|
|||
locale,
|
||||
i18nData: { "zh-CN": legacy, "en-US": legacy },
|
||||
appConfig: legacy,
|
||||
categoryList: p.categoryList,
|
||||
};
|
||||
},
|
||||
partialize: (s) => ({
|
||||
|
|
@ -70,6 +79,7 @@ export const useStore = create<StoreState>()(
|
|||
token: s.token,
|
||||
appConfig: s.i18nData?.[s.locale] ?? s.appConfig,
|
||||
supportLocales: s.supportLocales,
|
||||
categoryList: s.categoryList,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { useStore } from "zustand";
|
||||
import { LocaleKey } from "@/type";
|
||||
|
||||
// debounce
|
||||
export const debounce = (func: (...args: any[]) => void, delay: number) => {
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
|
|
|||
Loading…
Reference in New Issue