save
This commit is contained in:
parent
3f5380f9c4
commit
fabe5c893f
|
|
@ -45,6 +45,7 @@
|
||||||
"jest": "^27.4.3",
|
"jest": "^27.4.3",
|
||||||
"jest-resolve": "^27.4.2",
|
"jest-resolve": "^27.4.2",
|
||||||
"jest-watch-typeahead": "^1.0.0",
|
"jest-watch-typeahead": "^1.0.0",
|
||||||
|
"lenis": "^1.3.19",
|
||||||
"mime": "^4.0.7",
|
"mime": "^4.0.7",
|
||||||
"mini-css-extract-plugin": "^2.4.5",
|
"mini-css-extract-plugin": "^2.4.5",
|
||||||
"motion": "^12.23.25",
|
"motion": "^12.23.25",
|
||||||
|
|
@ -56,6 +57,7 @@
|
||||||
"postcss-preset-env": "^7.0.1",
|
"postcss-preset-env": "^7.0.1",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
"react-activation": "^0.13.4",
|
||||||
"react-app-polyfill": "^3.0.0",
|
"react-app-polyfill": "^3.0.0",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
|
@ -79,8 +81,7 @@
|
||||||
"webpack-manifest-plugin": "^4.0.2",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"workbox-webpack-plugin": "^6.4.1",
|
"workbox-webpack-plugin": "^6.4.1",
|
||||||
"zustand": "^5.0.11",
|
"zustand": "^5.0.11"
|
||||||
"react-activation": "^0.13.4"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node --stack-size=12800 --stack-trace-limit=20 scripts/start.js",
|
"dev": "node --stack-size=12800 --stack-trace-limit=20 scripts/start.js",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
cursor: all-scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ export type TimelineItem = {
|
||||||
type Props = {
|
type Props = {
|
||||||
items: TimelineItem[];
|
items: TimelineItem[];
|
||||||
height?: number;
|
height?: number;
|
||||||
|
refElement?: React.RefObject<HTMLDivElement | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateSinePath(
|
function generateSinePath(
|
||||||
|
|
@ -101,7 +102,7 @@ function computeContentWidth(items: TimelineItem[]): number {
|
||||||
return lastX + 280;
|
return lastX + 280;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SineWaveTimeline({ items, height: propHeight = 400 }: Props) {
|
export default function SineWaveTimeline({ items, height: propHeight = 400, refElement }: Props) {
|
||||||
if (!items?.length) return null;
|
if (!items?.length) return null;
|
||||||
|
|
||||||
const height = propHeight ?? 400;
|
const height = propHeight ?? 400;
|
||||||
|
|
@ -112,7 +113,7 @@ export default function SineWaveTimeline({ items, height: propHeight = 400 }: Pr
|
||||||
const bgPathD = generateCosinePath(contentWidth, height, AMPLITUDE * 0.9);
|
const bgPathD = generateCosinePath(contentWidth, height, AMPLITUDE * 0.9);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.scrollContainer}>
|
<div className={styles.scrollContainer} ref={refElement}>
|
||||||
<div
|
<div
|
||||||
className={styles.content}
|
className={styles.content}
|
||||||
style={{ width: contentWidth, height: `${expandedHeight}px` }}
|
style={{ width: contentWidth, height: `${expandedHeight}px` }}
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,22 @@
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 40px;
|
||||||
|
margin-top: 100px;
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
font-family: Source Han Sans, Source Han Sans;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #14355C;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 24px 40px;
|
||||||
|
background: #F0F2F4;
|
||||||
|
border-radius: 326px 326px 326px 326px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,15 +6,25 @@ type Data = {
|
||||||
content: string;
|
content: string;
|
||||||
statsData?: { num: string; label: string }[];
|
statsData?: { num: string; label: string }[];
|
||||||
backgroundImage?: string;
|
backgroundImage?: string;
|
||||||
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ParagraphSection({ data, children }: {data: Data, children?: React.ReactNode}) {
|
export default function ParagraphSection({ data, children }: { data: Data, children?: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<section className={styles.paragraphSection} style={{ backgroundImage: `url(${data.backgroundImage})` }}>
|
<section className={styles.paragraphSection} style={{ backgroundImage: `url(${data.backgroundImage})` }}>
|
||||||
<div className={`${styles.paragraphSectionContent} normal-p`}>
|
<div className={`${styles.paragraphSectionContent} normal-p`}>
|
||||||
<p><span className={styles.paragraphSectionTitle}>{data.title}</span>{data.content}</p>
|
<p><span className={styles.paragraphSectionTitle}>{data.title} </span>{data.content}</p>
|
||||||
</div>
|
</div>
|
||||||
{data.statsData && <StatsRow data={data.statsData} />}
|
{data.statsData && <StatsRow data={data.statsData} />}
|
||||||
|
{
|
||||||
|
data.tags && <div className={styles.tags}>
|
||||||
|
{
|
||||||
|
data.tags.map((tag) => (
|
||||||
|
<div key={tag} className={styles.tag}>{tag}</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,5 @@
|
||||||
.statLabel {
|
.statLabel {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: var(--stats-label-color);
|
color: var(--stats-label-color);
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useRef, useEffect, useState, useCallback } from 'react';
|
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
import styles from './index.module.css';
|
import styles from './index.module.css';
|
||||||
|
import { useStore } from '@/store';
|
||||||
|
|
||||||
type Data = {
|
type Data = {
|
||||||
tabItems: {
|
tabItems: {
|
||||||
|
|
@ -19,6 +20,8 @@ type Data = {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
export default function TopTabs({ data, activeIndex, setActiveIndex, className }: { data: Data, activeIndex: number, setActiveIndex: (index: number) => void, className?: string }) {
|
export default function TopTabs({ data, activeIndex, setActiveIndex, className }: { data: Data, activeIndex: number, setActiveIndex: (index: number) => void, className?: string }) {
|
||||||
|
const store = useStore();
|
||||||
|
const locale = useStore((state) => state.locale);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|
@ -99,9 +102,17 @@ export default function TopTabs({ data, activeIndex, setActiveIndex, className }
|
||||||
};
|
};
|
||||||
}, [activeIndex, data.tabItems.length, updateIndicatorPosition, updateScrollState]);
|
}, [activeIndex, data.tabItems.length, updateIndicatorPosition, updateScrollState]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('locale', locale);
|
||||||
|
updateIndicatorPosition();
|
||||||
|
}, [locale]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div ref={containerRef} className={styles.topTabsTabs}>
|
<div ref={containerRef} className={styles.topTabsTabs}
|
||||||
|
>
|
||||||
{canScrollLeft && (
|
{canScrollLeft && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
/* 隐藏滚动条 */
|
/* 隐藏滚动条 */
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
@ -159,3 +160,23 @@
|
||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topTabsContentItems {
|
||||||
|
width: 920px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topTabsContentItem {
|
||||||
|
list-style: disc;
|
||||||
|
font-family: Source Han Sans, Source Han Sans;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #222222;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: left;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import styles from './index.module.css';
|
||||||
import TopTabs from './TopTabs';
|
import TopTabs from './TopTabs';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
type Data = {
|
type Data = {
|
||||||
tabItems: {
|
tabItems: {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
|
@ -14,6 +15,7 @@ type Data = {
|
||||||
/** 以 mockData 为准 */
|
/** 以 mockData 为准 */
|
||||||
sideImage?: string;
|
sideImage?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
|
items?: {label: string}[];
|
||||||
}[]
|
}[]
|
||||||
backgroundImage?: string;
|
backgroundImage?: string;
|
||||||
titleDirection?: 'row' | 'column';
|
titleDirection?: 'row' | 'column';
|
||||||
|
|
@ -29,7 +31,6 @@ export default function TopTabsSection({ data, className }: { data: Data, classN
|
||||||
if (id && data.tabItems) {
|
if (id && data.tabItems) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const index = data.tabItems.findIndex((item) => item.tabName === id);
|
const index = data.tabItems.findIndex((item) => item.tabName === id);
|
||||||
console.log('index', index, id, data.tabItems)
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
setActiveIndex(index);
|
setActiveIndex(index);
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +41,18 @@ export default function TopTabsSection({ data, className }: { data: Data, classN
|
||||||
<section id={id} className={`${styles.topTabsSection} ${className}`} style={{ backgroundImage: `url(${data.backgroundImage})` }}>
|
<section id={id} className={`${styles.topTabsSection} ${className}`} style={{ backgroundImage: `url(${data.backgroundImage})` }}>
|
||||||
<TopTabs data={data} activeIndex={activeIndex} setActiveIndex={setActiveIndex} />
|
<TopTabs data={data} activeIndex={activeIndex} setActiveIndex={setActiveIndex} />
|
||||||
<div className={styles.topTabsContent}>
|
<div className={styles.topTabsContent}>
|
||||||
|
{
|
||||||
|
data.tabItems[activeIndex]?.items && (data.tabItems[activeIndex]?.items as any).length > 0 ?
|
||||||
|
<ul className={styles.topTabsContentItems}>
|
||||||
|
{
|
||||||
|
(data.tabItems[activeIndex]?.items as any).map((item: {label: string}) => (
|
||||||
|
<li key={item.label} className={styles.topTabsContentItem}>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</li>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul> : (
|
||||||
|
<>
|
||||||
<div className={styles.topTabsContentLeft}>
|
<div className={styles.topTabsContentLeft}>
|
||||||
<div className={styles.topTabsContentLeftHead}>
|
<div className={styles.topTabsContentLeftHead}>
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +72,10 @@ export default function TopTabsSection({ data, className }: { data: Data, classN
|
||||||
<div className={styles.topTabsContentRight}>
|
<div className={styles.topTabsContentRight}>
|
||||||
<img src={data.tabItems[activeIndex].sideImage} alt="side-image" />
|
<img src={data.tabItems[activeIndex].sideImage} alt="side-image" />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import ParagraphSection from "@/components/layout/ParagraphSection";
|
||||||
import { useStore } from "@/store";
|
import { useStore } from "@/store";
|
||||||
import Section from "@/components/layout/Section";
|
import Section from "@/components/layout/Section";
|
||||||
import SineWaveTimeline from "@/components/SineWaveTimeline";
|
import SineWaveTimeline from "@/components/SineWaveTimeline";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import Lenis from "lenis"
|
||||||
|
|
||||||
export default function AboutFounder() {
|
export default function AboutFounder() {
|
||||||
const appConfig = useStore((s) => s.appConfig);
|
const appConfig = useStore((s) => s.appConfig);
|
||||||
|
|
@ -81,14 +83,74 @@ export default function AboutFounder() {
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
)}
|
)}
|
||||||
|
<TimeLineComponent section4Data={section4Data} />
|
||||||
{section4Data && (
|
|
||||||
<section className={styles.section4Section} style={{ backgroundImage: section4Data?.backgroundImage ? `url(${section4Data?.backgroundImage})` : undefined }}>
|
|
||||||
<div className={styles.section4Title}>{section4Data?.title}</div>
|
|
||||||
<div className={styles.timelineWrapper}>
|
|
||||||
<SineWaveTimeline items={section4Data?.items ?? []} />
|
|
||||||
</div>
|
|
||||||
</section>)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function TimeLineComponent({ section4Data }: { section4Data: any }) {
|
||||||
|
const refElement = useRef<HTMLDivElement>(null);
|
||||||
|
const sectionRef = useRef<HTMLElement>(null);
|
||||||
|
const [isSectionInView, setIsSectionInView] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!sectionRef.current) return;
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
setIsSectionInView(entry.isIntersecting);
|
||||||
|
}, {
|
||||||
|
threshold: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(sectionRef.current);
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isSectionInView || !refElement.current) return;
|
||||||
|
|
||||||
|
const wrapper = refElement.current;
|
||||||
|
const content = wrapper.firstElementChild as HTMLElement | null;
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
const lenis = new Lenis({
|
||||||
|
wrapper,
|
||||||
|
content,
|
||||||
|
orientation: "horizontal",
|
||||||
|
gestureOrientation: "both",
|
||||||
|
smoothWheel: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let rafId = 0;
|
||||||
|
const raf = (time: number) => {
|
||||||
|
lenis.raf(time);
|
||||||
|
rafId = requestAnimationFrame(raf);
|
||||||
|
};
|
||||||
|
|
||||||
|
rafId = requestAnimationFrame(raf);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
lenis.destroy();
|
||||||
|
};
|
||||||
|
}, [isSectionInView]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{section4Data && (
|
||||||
|
<section
|
||||||
|
ref={sectionRef}
|
||||||
|
className={styles.section4Section}
|
||||||
|
style={{ backgroundImage: section4Data?.backgroundImage ? `url(${section4Data?.backgroundImage})` : undefined }}
|
||||||
|
>
|
||||||
|
<div className={styles.section4Title}>{section4Data?.title}</div>
|
||||||
|
<div className={styles.timelineWrapper}>
|
||||||
|
<SineWaveTimeline items={section4Data?.items ?? []} refElement={refElement} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -205,17 +205,20 @@
|
||||||
|
|
||||||
.featuresHeroTabRow {
|
.featuresHeroTabRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
/* gap: 100px; */
|
||||||
gap: 200px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.featuresHeroTab {
|
.featuresHeroTab {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
line-height: 60px;
|
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ function PlaceholderImage() {
|
||||||
|
|
||||||
export default function BusinessCommercialGroup() {
|
export default function BusinessCommercialGroup() {
|
||||||
const appConfig = useStore((s) => s.appConfig);
|
const appConfig = useStore((s) => s.appConfig);
|
||||||
|
const { viewDetail="查看详情" } = appConfig?.__global__?.others
|
||||||
const data = appConfig?.business?.commercialGroup;
|
const data = appConfig?.business?.commercialGroup;
|
||||||
const section3Data = data?.section3Data;
|
const section3Data = data?.section3Data;
|
||||||
|
|
||||||
|
|
@ -113,12 +114,12 @@ export default function BusinessCommercialGroup() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.twoColText}>
|
<div className={styles.twoColText}>
|
||||||
<p className={styles.twoColDesc}>{section2Data.tabItems[activeTabIndex]?.content}</p>
|
<p className={styles.twoColDesc} dangerouslySetInnerHTML={{ __html: section2Data.tabItems[activeTabIndex]?.content ?? "" }}></p>
|
||||||
<Link
|
<Link
|
||||||
to={section2Data.tabItems[activeTabIndex]?.path ?? "#"}
|
to={section2Data.tabItems[activeTabIndex]?.path ?? "#"}
|
||||||
className={styles.btnPrimary}
|
className={styles.btnPrimary}
|
||||||
>
|
>
|
||||||
查看详情
|
{viewDetail}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -186,7 +187,7 @@ export default function BusinessCommercialGroup() {
|
||||||
<div className={styles.propertyServicesTitle}>物业服务</div>
|
<div className={styles.propertyServicesTitle}>物业服务</div>
|
||||||
<p className={styles.propertyServicesSubtitle}>国内一流物业服务品牌</p>
|
<p className={styles.propertyServicesSubtitle}>国内一流物业服务品牌</p>
|
||||||
<Link to="/property-service" className={styles.propertyServicesBtn}>
|
<Link to="/property-service" className={styles.propertyServicesBtn}>
|
||||||
查看详情
|
{viewDetail}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ 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="查看详情" } = 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);
|
||||||
|
|
@ -132,7 +133,7 @@ export default function JoinCampus() {
|
||||||
<div className={styles.jobItem}>
|
<div className={styles.jobItem}>
|
||||||
<div className={styles.jobItemTitleRow}>
|
<div className={styles.jobItemTitleRow}>
|
||||||
<div className={styles.jobItemTitle}>{item.title}</div>
|
<div className={styles.jobItemTitle}>{item.title}</div>
|
||||||
<div className={styles.jobItemTitleRight}>查看详情 <RightOutlined /></div>
|
<div className={styles.jobItemTitleRight}>{viewDetail} <RightOutlined /></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.jobItemLabels}>
|
<div className={styles.jobItemLabels}>
|
||||||
{item.labels.map((label, index) => (
|
{item.labels.map((label, index) => (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue