This commit is contained in:
zhangjianjun 2026-04-01 17:22:35 +08:00
parent 91a40abb73
commit 116b567194
4 changed files with 100 additions and 59 deletions

View File

@ -1,3 +1,4 @@
import { useStore } from "@/store";
import styles from "./index.module.css"; import styles from "./index.module.css";
@ -14,34 +15,46 @@ type Data = {
} }
export default function JobPage({ data }: { data: Data }) { export default function JobPage({ data }: { data: Data }) {
const appConfig = useStore((s) => s.appConfig);
const {
jobType = "职位类别",
businessArea = "业务领域",
businessPlate = "所属板块",
recruitNumber = "招聘人数",
jobLocation = "工作地点",
jobDescription = "岗位描述",
requirement = "任职需求",
contact = "联系方式",
} = appConfig?.join?.campus.others || {};
const { back = "返回" } = appConfig?.__global__?.others
return ( return (
<div className={styles.jobPage}> <div className={styles.jobPage}>
<div className={styles.jobPageHeaderLine}> <div className={styles.jobPageHeaderLine}>
<div className={styles.jobPageHeaderLineBack} onClick={() => window.history.back()}> <div className={styles.jobPageHeaderLineBack} onClick={() => window.history.back()}>
<img src="/images/icons/icon-arrowleft.png" alt="arrowleft" style={{ width: "1.25rem", height: "1.25rem" }} /> <img src="/images/icons/icon-arrowleft.png" alt="arrowleft" style={{ width: "1.25rem", height: "1.25rem" }} />
<span></span> <span>{back}</span>
</div> </div>
</div> </div>
<div className={styles.jobPageHeader}> <div className={styles.jobPageHeader}>
<div className={styles.jobPageHeaderTitle}>{data.title}</div> <div className={styles.jobPageHeaderTitle}>{data.title}</div>
<div className={styles.jobPageHeaderTimeLine}> <div className={styles.jobPageHeaderTimeLine}>
<span>{data.jobType}</span> <span>{jobType}{data.jobType}</span>
<span>{data.businessArea}</span> <span>{businessArea}{data.businessArea}</span>
<span>{data.businessPlate}</span> <span>{businessPlate}{data.businessPlate}</span>
<span>{data.recruitNumber}</span> <span>{recruitNumber}{data.recruitNumber}</span>
<span>{data.jobLocation}</span> <span>{jobLocation}{data.jobLocation}</span>
</div> </div>
</div> </div>
<div className={styles.jobPageContent}> <div className={styles.jobPageContent}>
<div className={styles.jobPageContentTitle}></div> <div className={styles.jobPageContentTitle}>{jobDescription}</div>
<p className={styles.jobPageContentText} dangerouslySetInnerHTML={{ __html: data.content }}></p> <p className={styles.jobPageContentText} dangerouslySetInnerHTML={{ __html: data.content }}></p>
</div> </div>
<div className={styles.jobPageContent} style={{ marginTop: "3.125rem" }}> <div className={styles.jobPageContent} style={{ marginTop: "3.125rem" }}>
<div className={styles.jobPageContentTitle}></div> <div className={styles.jobPageContentTitle}>{requirement}</div>
<p className={styles.jobPageContentText} dangerouslySetInnerHTML={{ __html: data.requirement }}></p> <p className={styles.jobPageContentText} dangerouslySetInnerHTML={{ __html: data.requirement }}></p>
</div> </div>
<div className={styles.jobPageContent} style={{ marginTop: "3.125rem" }}> <div className={styles.jobPageContent} style={{ marginTop: "3.125rem" }}>
<div className={styles.jobPageContentTitle}></div> <div className={styles.jobPageContentTitle}>{contact}</div>
<p className={styles.jobPageContentText}>{data.contact}</p> <p className={styles.jobPageContentText}>{data.contact}</p>
</div> </div>
</div> </div>

View File

@ -9,24 +9,28 @@ import Pagination from "@/components/Pagination";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { debounce } from "@/utils"; import { debounce } from "@/utils";
import appApi from "@/api/app"; import appApi from "@/api/app";
import { LocaleKey } from "@/type";
type JobItem = { type JobItem = {
id: number; id: number;
title: string; title: string;
content: string; content: string;
labels: string[]; labels: string[];
lang: string lang: string;
city: string;
}; };
type SelectOption = { label: string; value: string }; type SelectOption = { label: string; value: string };
export default function JoinCampus() { export default function JoinCampus() {
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
const { viewDetail = "查看详情", noData = "暂无数据" } = appConfig?.__global__?.others const i18nData = useStore((s) => s.i18nData);
const { viewDetail = "查看详情", noData = "暂无数据", all = "全部" } = appConfig?.__global__?.others
const supportLocales = useStore((s) => s.supportLocales); const supportLocales = useStore((s) => s.supportLocales);
const categoryList = useStore((s) => s.categoryList); const categoryList = useStore((s) => s.categoryList);
const locale = useStore((s) => s.locale); const locale = useStore((s) => s.locale);
const data = appConfig?.join?.campus; const data = appConfig?.join?.campus;
const cityMapList = appConfig?.cityMap.citys;
const banner = data?.banner; const banner = data?.banner;
const others = data?.others; const others = data?.others;
@ -66,6 +70,7 @@ export default function JoinCampus() {
content: item.description, content: item.description,
labels, labels,
lang: item.lang, lang: item.lang,
city: item.city,
}; };
}); });
setJobList(items || []); setJobList(items || []);
@ -73,16 +78,20 @@ export default function JoinCampus() {
}); });
}, 500), []); }, 500), []);
const getTypes = useCallback(() => {
const getTypes = useCallback((locale: string) => {
const isZh = locale.startsWith('zh');
const jobTypeOptions: SelectOption[] = const jobTypeOptions: SelectOption[] =
categoryList?.filter((item: any) => item.type === 'job_type').map((item: any) => ({ label: item.name, value: String(item.id) })) ?? []; categoryList?.filter((item: any) => item.type === 'job_type').map((item: any) => ({ label: isZh ? item.name : item.name_en, value: String(item.id) })) ?? [];
const businessAreaOptions: SelectOption[] = const businessAreaOptions: SelectOption[] =
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: isZh ? item.name : item.name_en, 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: isZh ? item.name : item.name_en, value: String(item.id) })) ?? [];
setJobTypeOptions([{ label: '全部', value: '' }, ...jobTypeOptions]); const allText = i18nData?.[locale as LocaleKey]?.__global__?.others?.all;
setBusinessAreaOptions([{ label: '全部', value: '' }, ...businessAreaOptions]); setJobTypeOptions([{ label: allText, value: '' }, ...jobTypeOptions]);
setBusinessPlateOptions([{ label: '全部', value: '' }, ...businessPlateOptions]); setBusinessAreaOptions([{ label: allText, value: '' }, ...businessAreaOptions]);
setBusinessPlateOptions([{ label: allText, value: '' }, ...businessPlateOptions]);
}, [categoryList]); }, [categoryList]);
useEffect(() => { useEffect(() => {
@ -90,8 +99,8 @@ export default function JoinCampus() {
}, [searchValue, jobType, businessArea, businessPlate, page, size]); }, [searchValue, jobType, businessArea, businessPlate, page, size]);
useEffect(() => { useEffect(() => {
getTypes(); getTypes(locale);
}, []); }, [locale, categoryList]);
const handleReset = useCallback(() => { const handleReset = useCallback(() => {
setSearchValue(''); setSearchValue('');
@ -140,6 +149,9 @@ export default function JoinCampus() {
{item.labels.map((label, index) => ( {item.labels.map((label, index) => (
<div key={index} className={styles.jobItemLabel}>&nbsp;&nbsp;&nbsp;{label}</div> <div key={index} className={styles.jobItemLabel}>&nbsp;&nbsp;&nbsp;{label}</div>
))} ))}
&nbsp;&nbsp;&nbsp;{
cityMapList.find((city: any) => city.untranslate_title === item.city)?.showName ?? item.city
}
</div> </div>
<div className={styles.jobItemContent}>{others?.jobDuty ?? "工作职责"}{item.content}</div> <div className={styles.jobItemContent}>{others?.jobDuty ?? "工作职责"}{item.content}</div>
</div></Link> </div></Link>
@ -190,6 +202,8 @@ type SelectFormItemProps = {
onChange: (value: string) => void; onChange: (value: string) => void;
} }
function SelectFormItem({ value, options, label, onChange }: SelectFormItemProps) { function SelectFormItem({ value, options, label, onChange }: SelectFormItemProps) {
const appConfig = useStore((s) => s.appConfig);
const { noData = "暂无数据", all = "全部" } = appConfig?.__global__?.others
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const selectId = `select-${label.replace(/\s/g, "-")}`; const selectId = `select-${label.replace(/\s/g, "-")}`;
@ -203,8 +217,8 @@ function SelectFormItem({ value, options, label, onChange }: SelectFormItemProps
open={open} open={open}
onOpenChange={setOpen} onOpenChange={setOpen}
showSearch showSearch
placeholder="全部" placeholder={all}
notFoundContent="无数据" notFoundContent={noData}
optionFilterProp="label" optionFilterProp="label"
filterOption={(input, opt) => filterOption={(input, opt) =>
(opt?.label ?? "").toString().toLowerCase().includes(input.toLowerCase()) (opt?.label ?? "").toString().toLowerCase().includes(input.toLowerCase())
@ -213,7 +227,7 @@ function SelectFormItem({ value, options, label, onChange }: SelectFormItemProps
value={value} value={value}
onChange={v => onChange(v ?? "")} onChange={v => onChange(v ?? "")}
prefix={ prefix={
<div style={{ marginRight: '5px', marginTop: '5px'}}> <div style={{ marginRight: '5px', marginTop: '5px' }}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><defs><clipPath id="master_svg0_199_80367"><rect x="0" y="0" width="24" height="24" rx="0" /></clipPath></defs><g clip-path="url(#master_svg0_199_80367)"><path d="M14.05733975,21.081789C13.96563975,21.081789,13.87371875,21.059202,13.79029575,21.013798L9.675932450000001,18.777245999999998C9.495936350000001,18.679411,9.38386445,18.490966,9.383838650000001,18.286098000000003L9.383838650000001,10.00705L3.90227196,4.8075126C3.8966805,4.8021449,3.89131287,4.7967771,3.88594502,4.7914094C3.5768535242,4.4731479,3.488733269,4.003472,3.661618777,3.59463C3.83450428,3.18578807,4.23283428,2.921874900903624,4.6763426500000005,2.921875L19.32419875,2.921875C19.76792875,2.921875,20.16625975,3.18601185,20.33914575,3.59463C20.51203175,4.0032483,20.42390975,4.4729244999999995,20.11482075,4.7914094C20.10945275,4.7970008,20.10408575,4.8023684,20.09849375,4.8075126L14.61670375,10.00705L14.61670375,20.52265C14.61670375,20.719915,14.51292775,20.90242,14.34339575,21.003284C14.25693675,21.054735,14.15817675,21.081863,14.05756575,21.081789L14.05733975,21.081789ZM10.50211475,17.953525L13.49820045,19.58218L13.49820045,9.7668447C13.49820045,9.6134176,13.56127165,9.4666991,13.67265175,9.361134100000001L19.282148749999998,4.0403748L4.71838995,4.0403748L10.32811065,9.361134100000001C10.439490750000001,9.4666991,10.50256155,9.6134176,10.50256155,9.7668447L10.50256155,17.953522L10.50211475,17.953525ZM19.97973075,11.2396154C19.97973075,10.9307489,19.72946175,10.6804776,19.42059275,10.6804776L16.72979575,10.6804776C16.42092775,10.6804776,16.17065775,10.930747,16.17065775,11.2396154C16.17065775,11.5484848,16.42092775,11.7987537,16.72979575,11.7987537L19.42059275,11.7987537C19.72946175,11.7987537,19.97973075,11.5484848,19.97973075,11.2396154ZM19.97973075,14.074223C19.97973075,13.765355,19.72946175,13.515085,19.42059275,13.515085L16.72979575,13.515085C16.42092775,13.515085,16.17065775,13.765354,16.17065775,14.074223C16.17065775,14.383092,16.42092775,14.633361,16.72979575,14.633361L19.42059275,14.633361C19.72946175,14.633361,19.97973075,14.383092,19.97973075,14.074223ZM19.97973075,16.908830000000002C19.97973075,16.599961999999998,19.72946175,16.349690000000002,19.42059275,16.349690000000002L16.72979575,16.349690000000002C16.42092775,16.349690000000002,16.17065775,16.599961999999998,16.17065775,16.908830000000002C16.17065775,17.217699,16.42092775,17.467967,16.72979575,17.467967L19.42059275,17.467967C19.72946175,17.467967,19.97973075,17.217699,19.97973075,16.908830000000002Z" fill="#222222" fill-opacity="1" /></g></svg> <svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><defs><clipPath id="master_svg0_199_80367"><rect x="0" y="0" width="24" height="24" rx="0" /></clipPath></defs><g clip-path="url(#master_svg0_199_80367)"><path d="M14.05733975,21.081789C13.96563975,21.081789,13.87371875,21.059202,13.79029575,21.013798L9.675932450000001,18.777245999999998C9.495936350000001,18.679411,9.38386445,18.490966,9.383838650000001,18.286098000000003L9.383838650000001,10.00705L3.90227196,4.8075126C3.8966805,4.8021449,3.89131287,4.7967771,3.88594502,4.7914094C3.5768535242,4.4731479,3.488733269,4.003472,3.661618777,3.59463C3.83450428,3.18578807,4.23283428,2.921874900903624,4.6763426500000005,2.921875L19.32419875,2.921875C19.76792875,2.921875,20.16625975,3.18601185,20.33914575,3.59463C20.51203175,4.0032483,20.42390975,4.4729244999999995,20.11482075,4.7914094C20.10945275,4.7970008,20.10408575,4.8023684,20.09849375,4.8075126L14.61670375,10.00705L14.61670375,20.52265C14.61670375,20.719915,14.51292775,20.90242,14.34339575,21.003284C14.25693675,21.054735,14.15817675,21.081863,14.05756575,21.081789L14.05733975,21.081789ZM10.50211475,17.953525L13.49820045,19.58218L13.49820045,9.7668447C13.49820045,9.6134176,13.56127165,9.4666991,13.67265175,9.361134100000001L19.282148749999998,4.0403748L4.71838995,4.0403748L10.32811065,9.361134100000001C10.439490750000001,9.4666991,10.50256155,9.6134176,10.50256155,9.7668447L10.50256155,17.953522L10.50211475,17.953525ZM19.97973075,11.2396154C19.97973075,10.9307489,19.72946175,10.6804776,19.42059275,10.6804776L16.72979575,10.6804776C16.42092775,10.6804776,16.17065775,10.930747,16.17065775,11.2396154C16.17065775,11.5484848,16.42092775,11.7987537,16.72979575,11.7987537L19.42059275,11.7987537C19.72946175,11.7987537,19.97973075,11.5484848,19.97973075,11.2396154ZM19.97973075,14.074223C19.97973075,13.765355,19.72946175,13.515085,19.42059275,13.515085L16.72979575,13.515085C16.42092775,13.515085,16.17065775,13.765354,16.17065775,14.074223C16.17065775,14.383092,16.42092775,14.633361,16.72979575,14.633361L19.42059275,14.633361C19.72946175,14.633361,19.97973075,14.383092,19.97973075,14.074223ZM19.97973075,16.908830000000002C19.97973075,16.599961999999998,19.72946175,16.349690000000002,19.42059275,16.349690000000002L16.72979575,16.349690000000002C16.42092775,16.349690000000002,16.17065775,16.599961999999998,16.17065775,16.908830000000002C16.17065775,17.217699,16.42092775,17.467967,16.72979575,17.467967L19.42059275,17.467967C19.72946175,17.467967,19.97973075,17.217699,19.97973075,16.908830000000002Z" fill="#222222" fill-opacity="1" /></g></svg>
</div> </div>
} }

View File

@ -8,6 +8,7 @@ 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"; import ScrollReveal from "@/components/ScrollReveal";
import { dateFormat } from "@/utils";
type NewsItem = { type NewsItem = {
id: number; id: number;
title: string; title: string;
@ -33,10 +34,11 @@ export default function NewsPublic() {
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]); const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
const [searchValue, setSearchValue] = useState(""); const [searchValue, setSearchValue] = useState("");
const handleSearch = useCallback(() => { const handleSearch = useCallback(() => {
appApi.getNewsList({ page, size, sort: "create_time DESC", title: searchValue, appApi.getNewsList({
page, size, sort: "create_time DESC", title: searchValue,
category_id: categoryId, category_id: categoryId,
}).then((res) => { }).then((res) => {
const data = res.data.items.map((item:any) => { const data = res.data.items.map((item: any) => {
return { return {
id: item.id, id: item.id,
title: item.title, title: item.title,
@ -90,38 +92,40 @@ 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}> <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}`}
className={styles.newItem} className={styles.newItem}
> >
{item.image ? ( {item.image ? (
<img src={item.image} alt={item.title} /> <img src={item.image} alt={item.title} />
) : ( ) : (
<video <video
ref={(el) => { ref={(el) => {
videoRefs.current[index] = el; videoRefs.current[index] = el;
}} }}
src={item.video} src={item.video}
onMouseEnter={() => { onMouseEnter={() => {
const v = videoRefs.current[index]; const v = videoRefs.current[index];
v?.play(); v?.play();
}} }}
onMouseLeave={() => { onMouseLeave={() => {
const v = videoRefs.current[index]; const v = videoRefs.current[index];
if (v) { if (v) {
v.pause(); v.pause();
v.currentTime = 0; v.currentTime = 0;
} }
}} }}
/> />
)} )}
<div className={styles.newItemContent}> <div className={styles.newItemContent}>
<div className={styles.newItemTitle}>{item.title}</div> <div className={styles.newItemTitle}>{item.title}</div>
<div className={styles.newItemCreateTime}>{item.createTime}</div> <div className={styles.newItemCreateTime}>
</div> {dateFormat(item.createTime, "yyyy-MM-dd")}
</Link> </div>
</div>
</Link>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>

View File

@ -168,7 +168,15 @@ export function parsePageConfig(items: RawPageItem[]): {
} catch(err) { } catch(err) {
console.error('parse topMenuConfig error', err) console.error('parse topMenuConfig error', err)
} }
console.log('topMenuJson', topMenuJson)
const cityMapConfig = filtered.find((it) => it.tags === "city-map");
let cityMapJson: any = null
try {
cityMapJson = JSON.parse(cityMapConfig?.content ?? "[]");
} catch(err) {
console.error('parse cityMapConfig error', err)
}
// const zhNavItems = buildNavItems(menuItems, "ZH", parsedContentMap); // const zhNavItems = buildNavItems(menuItems, "ZH", parsedContentMap);
// const enNavItems = buildNavItems(menuItems, "EN", parsedContentMap); // const enNavItems = buildNavItems(menuItems, "EN", parsedContentMap);
@ -202,6 +210,8 @@ export function parsePageConfig(items: RawPageItem[]): {
zhConfig.navItems = topMenuJson.ZH.items; zhConfig.navItems = topMenuJson.ZH.items;
enConfig.navItems = topMenuJson.EN.items; enConfig.navItems = topMenuJson.EN.items;
zhConfig.cityMap = cityMapJson.ZH;
enConfig.cityMap = cityMapJson.EN;
deepFallback(enConfig, zhConfig); deepFallback(enConfig, zhConfig);
return { "zh-CN": zhConfig, "en-US": enConfig }; return { "zh-CN": zhConfig, "en-US": enConfig };