save
This commit is contained in:
parent
8502fdc6c3
commit
aa8358c91e
10
src/App.tsx
10
src/App.tsx
|
|
@ -4,7 +4,8 @@ import "./App.css"
|
||||||
import routes from "@/Routes";
|
import routes from "@/Routes";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import appApi from "@/api/app";
|
import appApi from "@/api/app";
|
||||||
import { useStore, type I18nData } from "@/store";
|
import { useStore } from "@/store";
|
||||||
|
import type { I18nData, SupportLocale } from "@/type";
|
||||||
|
|
||||||
function AppRoutes() {
|
function AppRoutes() {
|
||||||
return useRoutes(routes);
|
return useRoutes(routes);
|
||||||
|
|
@ -36,7 +37,12 @@ function App() {
|
||||||
const data = res.data as I18nData;
|
const data = res.data as I18nData;
|
||||||
useStore.getState().setAppConfig(data);
|
useStore.getState().setAppConfig(data);
|
||||||
const locale = useStore.getState().locale;
|
const locale = useStore.getState().locale;
|
||||||
const config = data[locale] ?? data.zhCN;
|
const config = data[locale] ?? data["zh-CN"];
|
||||||
|
const supportLocales: SupportLocale[] = [
|
||||||
|
{ key: "zh-CN", label: "中文" },
|
||||||
|
{ key: "en-US", label: "English" },
|
||||||
|
];
|
||||||
|
useStore.getState().setSupportLocales(supportLocales);
|
||||||
initState(config);
|
initState(config);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* 接口返回数据结构
|
* 接口返回数据结构
|
||||||
* mockData: {
|
* mockData: {
|
||||||
* zhCN: {...},
|
* "zh-CN": {...},
|
||||||
* en: {...}
|
* "en-US": {...}
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type NavChild = { path: string; label: string };
|
import type { NavChild, NavItem } from "@/type";
|
||||||
export type NavItem = { path: string; label: string; children?: NavChild[]; index?: boolean };
|
|
||||||
export type LocaleKey = "zhCN" | "en";
|
|
||||||
|
|
||||||
const zhCN = {
|
const zhCN = {
|
||||||
companyName: "银泰",
|
companyName: "银泰",
|
||||||
|
|
@ -72,10 +70,6 @@ const zhCN = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] as NavItem[],
|
] as NavItem[],
|
||||||
langMenuItems: [
|
|
||||||
{ key: "zhCN", label: "中文" },
|
|
||||||
{ key: "en", label: "English" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
footer: {
|
footer: {
|
||||||
|
|
@ -309,7 +303,44 @@ const zhCN = {
|
||||||
backgroundImage: '',
|
backgroundImage: '',
|
||||||
sideImage: "/images/bg-invest-group.png",
|
sideImage: "/images/bg-invest-group.png",
|
||||||
content: "银泰集团在社会责任方面有着丰富的经验和深厚的实力,致力于打造高品质的商业空间,引领现代消费体验。",
|
content: "银泰集团在社会责任方面有着丰富的经验和深厚的实力,致力于打造高品质的商业空间,引领现代消费体验。",
|
||||||
}
|
},
|
||||||
|
section3Data: {
|
||||||
|
title: "社会职务",
|
||||||
|
backgroundImage: '/images/bg-overview.png',
|
||||||
|
columns: [
|
||||||
|
[ // 第 1 列
|
||||||
|
{ title: '第十一、十三届全国政协委员' },
|
||||||
|
{ title: '第十三届全国政协提案委员会委员' },
|
||||||
|
{ title: '第十四、十五、十六届中国致公党中央常委' },
|
||||||
|
{ title: '第一届浙商总会执行会长' },
|
||||||
|
],
|
||||||
|
[ // 第 2 列
|
||||||
|
{ title: '第一届甬商总会会长' },
|
||||||
|
{ title: '北京浙江企业商会终身名誉会长' },
|
||||||
|
{ title: '西安市人民政府经济顾问' },
|
||||||
|
{ title: '中国企业家俱乐部理事' },
|
||||||
|
],
|
||||||
|
[ // 第 3 列
|
||||||
|
{ title: '桃花源生态保护基金会执行主席' },
|
||||||
|
{ title: '银泰公益基金会创始人兼荣誉理事长' },
|
||||||
|
{ title: '中国宋庆龄基金会第六届理事会理事' },
|
||||||
|
{ title: '爱佑慈善基金会发起理事' },
|
||||||
|
{ title: '致福慈善基金会副理事长' },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
},
|
||||||
|
section4Data: {
|
||||||
|
title: "荣誉奖项",
|
||||||
|
backgroundImage: '',
|
||||||
|
items: [
|
||||||
|
{ year: '2020年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
{ year: '2019年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
{ year: '2018年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
{ year: '2017年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
{ year: '2016年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
{ year: '2015年', children: ["2015年度“影响·2015中国公益100人”", "2015年度“中国社会十大推动者”"]},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -975,10 +1006,6 @@ const en: typeof zhCN = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] as NavItem[],
|
] as NavItem[],
|
||||||
langMenuItems: [
|
|
||||||
{ key: "zhCN", label: "中文" },
|
|
||||||
{ key: "en", label: "English" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
...zhCN.footer,
|
...zhCN.footer,
|
||||||
|
|
@ -1002,5 +1029,5 @@ const en: typeof zhCN = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LocaleConfig = typeof zhCN;
|
export type LocaleConfig = typeof zhCN;
|
||||||
const mockData = { zhCN, en };
|
const mockData = { "zh-CN": zhCN, "en-US": en };
|
||||||
export default mockData;
|
export default mockData;
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,11 @@ import "swiper/css/effect-fade";
|
||||||
import styles from "./Banner.module.css";
|
import styles from "./Banner.module.css";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useStore } from "@/store";
|
import { useStore } from "@/store";
|
||||||
|
import type { BannerConfig } from "@/type";
|
||||||
|
|
||||||
const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)";
|
const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)";
|
||||||
|
|
||||||
|
export type { BannerConfig } from "@/type";
|
||||||
export type BannerConfig = {
|
|
||||||
title?: string;
|
|
||||||
content?: string;
|
|
||||||
subtitle?: string;
|
|
||||||
largeContent?: string;
|
|
||||||
titleSize?: "large" | "medium" | string;
|
|
||||||
showBreadcrumb?: boolean;
|
|
||||||
backgroundImage?: string | string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.contentItemSubtitle {
|
.contentItemSubtitle {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
@ -93,6 +94,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentItemContentWrapper {
|
.contentItemContentWrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
height: 0;
|
height: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: all var(--duration) ease-in-out;
|
transition: all var(--duration) ease-in-out;
|
||||||
|
|
@ -100,11 +102,12 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 60px 0;
|
/* padding: 60px 0; */
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
.contentItemContent {
|
.contentItemContent {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-top: 40px;
|
/* margin-top: 40px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentItemLinks {
|
.contentItemLinks {
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ function FooterLower({
|
||||||
<div className={styles.footerLowerLinks}>
|
<div className={styles.footerLowerLinks}>
|
||||||
{lowerLinks.map((link, index) => (
|
{lowerLinks.map((link, index) => (
|
||||||
< >
|
< >
|
||||||
<Link to={link.path} className={styles.footerLowerLink}>
|
<Link key={index} to={link.path} className={styles.footerLowerLink}>
|
||||||
{link.label}
|
{link.label}
|
||||||
</Link>
|
</Link>
|
||||||
{index !== lowerLinks.length - 1 && <span>·</span>}
|
{index !== lowerLinks.length - 1 && <span>·</span>}
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropPanelLink {
|
.dropPanelLink {
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,29 @@ import { Dropdown } from "antd";
|
||||||
import styles from "./Header.module.css";
|
import styles from "./Header.module.css";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useStore } from "@/store";
|
import { useStore } from "@/store";
|
||||||
import type { NavChild } from "@/api/mockData";
|
import type { NavChild, LocaleKey, SupportLocale } from "@/type";
|
||||||
import type { LocaleKey } from "@/store";
|
|
||||||
|
|
||||||
const DEFAULT_NAV_ITEMS: { path: string; label: string; children?: NavChild[] }[] = [];
|
const DEFAULT_NAV_ITEMS: { path: string; label: string; children?: NavChild[] }[] = [];
|
||||||
const DEFAULT_LANG_ITEMS = [{ key: "zh", label: "中文" }, { key: "en", label: "English" }];
|
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const appConfig = useStore((s) => s.appConfig);
|
const appConfig = useStore((s) => s.appConfig);
|
||||||
const locale = useStore((s) => s.locale);
|
const locale = useStore((s) => s.locale);
|
||||||
const setLocale = useStore((s) => s.setLocale);
|
const setLocale = useStore((s) => s.setLocale);
|
||||||
|
const supportLocales = useStore((s) => s.supportLocales);
|
||||||
|
|
||||||
const navItems = appConfig?.header?.navItems?.filter((item) => !item.index) ?? DEFAULT_NAV_ITEMS;
|
const navItems = appConfig?.header?.navItems?.filter((item) => !item.index) ?? DEFAULT_NAV_ITEMS;
|
||||||
const langMenuItems = appConfig?.header?.langMenuItems ?? DEFAULT_LANG_ITEMS;
|
const langMenuItems: SupportLocale[] = supportLocales || [];
|
||||||
const logo = appConfig?.logo ?? "/images/logo.png";
|
const logo = appConfig?.logo ?? "/images/logo.png";
|
||||||
|
|
||||||
const [activeNav, setActiveNav] = useState("");
|
const [activeNav, setActiveNav] = useState("");
|
||||||
const [showDropPanel, setShowDropPanel] = useState(false);
|
const [showDropPanel, setShowDropPanel] = useState(false);
|
||||||
const [hoverElLeft, setHoverElLeft] = useState(0);
|
const [hoverElLeft, setHoverElLeft] = useState(0);
|
||||||
const handleNavEnter = (e: any, path: string) => {
|
const handleNavEnter = (e: any, path: string) => {
|
||||||
// left + 元素宽度的一半
|
|
||||||
const left = e.target.offsetLeft;
|
const left = e.target.offsetLeft;
|
||||||
setHoverElLeft(left);
|
// 计算元素宽度
|
||||||
|
const width = e.target.offsetWidth;
|
||||||
|
setHoverElLeft(left + width / 2);
|
||||||
setActiveNav(path);
|
setActiveNav(path);
|
||||||
setShowDropPanel(true);
|
setShowDropPanel(true);
|
||||||
}
|
}
|
||||||
|
|
@ -87,9 +87,9 @@ export default function Header() {
|
||||||
</Link>
|
</Link>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{
|
menu={{
|
||||||
items: langMenuItems.map((item) => ({
|
items: langMenuItems.map((item: SupportLocale) => ({
|
||||||
...item,
|
...item,
|
||||||
onClick: () => setLocale(item.key as LocaleKey),
|
onClick: () => setLocale(item.key),
|
||||||
})),
|
})),
|
||||||
}}
|
}}
|
||||||
placement="bottomRight"
|
placement="bottomRight"
|
||||||
|
|
@ -97,7 +97,7 @@ export default function Header() {
|
||||||
>
|
>
|
||||||
<button className={styles.langTrigger} type="button">
|
<button className={styles.langTrigger} type="button">
|
||||||
<span style={{ fontSize: "18px" }}>
|
<span style={{ fontSize: "18px" }}>
|
||||||
{langMenuItems.find((i) => i.key === locale)?.label ?? "中文"}
|
{langMenuItems.find((i: SupportLocale) => i.key === locale)?.label ?? "中文"}
|
||||||
</span>
|
</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><g transform="matrix(0,1,-1,0,24,-24)"><path d="M46.720364062499996,30.4020875C46.720364062499996,30.4020875,45.6597041625,31.462787499999997,45.6597041625,31.462787499999997C45.6597041625,31.462787499999997,39.8815300725,25.6845775,39.8815300725,25.6845775C39.4910054655,25.2940474,39.4910053615,24.6608877,39.8815300725,24.2703576C39.8815300725,24.2703576,45.6597041625,18.4921875,45.6597041625,18.4921875C45.6597041625,18.4921875,46.720364062499996,19.5528475,46.720364062499996,19.5528475C46.720364062499996,19.5528475,41.2957440625,24.9774675,41.2957440625,24.9774675C41.2957440625,24.9774675,46.720364062499996,30.4020875,46.720364062499996,30.4020875C46.720364062499996,30.4020875,46.720364062499996,30.4020875,46.720364062499996,30.4020875Z" fillRule="evenodd" fill="#FFFFFF" fillOpacity="1" transform="matrix(-1,0,0,-1,79.173828125,36.984375)" /></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.1" width="24" height="24" viewBox="0 0 24 24"><g transform="matrix(0,1,-1,0,24,-24)"><path d="M46.720364062499996,30.4020875C46.720364062499996,30.4020875,45.6597041625,31.462787499999997,45.6597041625,31.462787499999997C45.6597041625,31.462787499999997,39.8815300725,25.6845775,39.8815300725,25.6845775C39.4910054655,25.2940474,39.4910053615,24.6608877,39.8815300725,24.2703576C39.8815300725,24.2703576,45.6597041625,18.4921875,45.6597041625,18.4921875C45.6597041625,18.4921875,46.720364062499996,19.5528475,46.720364062499996,19.5528475C46.720364062499996,19.5528475,41.2957440625,24.9774675,41.2957440625,24.9774675C41.2957440625,24.9774675,46.720364062499996,30.4020875,46.720364062499996,30.4020875C46.720364062499996,30.4020875,46.720364062499996,30.4020875,46.720364062499996,30.4020875Z" fillRule="evenodd" fill="#FFFFFF" fillOpacity="1" transform="matrix(-1,0,0,-1,79.173828125,36.984375)" /></g></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.section {
|
.section {
|
||||||
background: rgba(255,255,255,0.6);
|
background: rgba(255, 255, 255, 0.6);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -26,38 +26,45 @@
|
||||||
aspect-ratio: 680 / 800;
|
aspect-ratio: 680 / 800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.images img{
|
|
||||||
|
.images img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 680 / 800;
|
aspect-ratio: 680 / 800;
|
||||||
background: rgba(0,0,0,0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageItem {
|
.images .imageItem {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.imageOverlay {
|
.imageOverlay {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageItem:hover .imageOverlay {
|
.images .imageItem:hover .imageOverlay {
|
||||||
top: 0;
|
top: 0;
|
||||||
background: rgba(20,53,92,0.8);
|
background: rgba(20, 53, 92, 0.8);
|
||||||
|
|
||||||
.imageOverlayDesc {
|
.imageOverlayDesc {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageOverlayTitle span {
|
.imageOverlayTitle span {
|
||||||
border-bottom: 3px solid #FFFFFF;
|
border-bottom: 3px solid #FFFFFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageMask {
|
.images .imageMask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0,0,0,0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageOverlay {
|
.images .imageOverlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 78%;
|
top: 78%;
|
||||||
|
|
@ -67,33 +74,38 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 3.75rem 2.5rem;
|
padding: 3.75rem 2.5rem;
|
||||||
transition: top 0.3s ease-in-out;
|
transition: top 0.3s ease-in-out;
|
||||||
|
|
||||||
.imageOverlayDesc {
|
.imageOverlayDesc {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageOverlayTitle {
|
.images .imageOverlayTitle {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
height: 4.375rem;
|
height: 4.375rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.3);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageOverlayTitle span {
|
.images .imageOverlayTitle span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 4.4375rem;
|
height: 4.4375rem;
|
||||||
border-bottom: 3px solid #14355C;
|
border-bottom: 3px solid #14355C;
|
||||||
}
|
}
|
||||||
|
|
||||||
.images .imageOverlayDesc {
|
.images .imageOverlayDesc {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
margin-top: 3.125rem;
|
margin-top: 3.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sectionFounder{
|
.sectionFounder {
|
||||||
padding: 6.25rem 0 9.375rem;
|
padding: 6.25rem 0 9.375rem;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.founderIntroduction h2 {
|
.founderIntroduction h2 {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|
@ -101,6 +113,7 @@
|
||||||
margin-bottom: 3.125rem;
|
margin-bottom: 3.125rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.founderIntroduction p {
|
.founderIntroduction p {
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
@ -109,22 +122,25 @@
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.founderPhoto{
|
|
||||||
|
.founderPhoto {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 6.25rem;
|
gap: 6.25rem;
|
||||||
margin-top: 6.25rem;
|
margin-top: 6.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.founderPhoto img{
|
.founderPhoto img {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 680 / 800;
|
aspect-ratio: 680 / 800;
|
||||||
}
|
}
|
||||||
.founderPhotoContent{
|
|
||||||
|
.founderPhotoContent {
|
||||||
padding-top: 6.25rem;
|
padding-top: 6.25rem;
|
||||||
padding-right: 6.25rem;
|
padding-right: 6.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.founderPhotoContent p {
|
.founderPhotoContent p {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
@ -133,6 +149,28 @@
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section3Content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
align-items: start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section3Item {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 22px;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.section {
|
.section {
|
||||||
padding: 2rem 1rem;
|
padding: 2rem 1rem;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Banner from "@/components/Banner";
|
||||||
import styles from "./Founder.module.css";
|
import styles from "./Founder.module.css";
|
||||||
import ParagraphSection from "@/components/layout/ParagraphSection";
|
import ParagraphSection from "@/components/layout/ParagraphSection";
|
||||||
import { useStore } from "@/store";
|
import { useStore } from "@/store";
|
||||||
|
import Section from "@/components/layout/Section";
|
||||||
|
|
||||||
export default function AboutFounder() {
|
export default function AboutFounder() {
|
||||||
const appConfig = useStore((s) => s.appConfig);
|
const appConfig = useStore((s) => s.appConfig);
|
||||||
|
|
@ -10,6 +11,8 @@ export default function AboutFounder() {
|
||||||
const banner = founder?.banner;
|
const banner = founder?.banner;
|
||||||
const section1Data = founder?.section1Data;
|
const section1Data = founder?.section1Data;
|
||||||
const section2Data = founder?.section2Data;
|
const section2Data = founder?.section2Data;
|
||||||
|
const section3Data = founder?.section3Data;
|
||||||
|
const section4Data = founder?.section4Data;
|
||||||
|
|
||||||
if (!founder) return null;
|
if (!founder) return null;
|
||||||
|
|
||||||
|
|
@ -63,6 +66,26 @@ export default function AboutFounder() {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 社会职务 */}
|
||||||
|
<Section title={section3Data?.title ?? ""} titleColor="#fff" background={section3Data?.backgroundImage ?? ""}>
|
||||||
|
<div className={styles.section3Content}>
|
||||||
|
{Array.from({
|
||||||
|
length: Math.max(0, ...(section3Data?.columns?.map((c) => c.length) ?? [0])),
|
||||||
|
}).flatMap((_, rowIndex) =>
|
||||||
|
(section3Data?.columns ?? []).map((colItems, colIndex) => (
|
||||||
|
<div key={`${rowIndex}-${colIndex}`} className={styles.section3Item}>
|
||||||
|
{colItems[rowIndex] ? <li>{colItems[rowIndex].title}</li> : null}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 荣誉奖项 */}
|
||||||
|
<Section title={section4Data?.title} background={section4Data?.backgroundImage ?? ""}>
|
||||||
|
|
||||||
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export default function BusinessCommercialGroup() {
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
src={tabItems[activeTabIndex]?.image}
|
src={tabItems[activeTabIndex]?.image}
|
||||||
alt="in77 杭州湖滨银泰"
|
alt=""
|
||||||
onError={() => setIn77ImgError(true)}
|
onError={() => setIn77ImgError(true)}
|
||||||
style={{ width: "100%", height: "800px" }}
|
style={{ width: "100%", height: "800px" }}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,36 @@ import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
import type mockData from "@/api/mockData";
|
import type mockData from "@/api/mockData";
|
||||||
|
import type { LocaleKey, SupportLocale } from "@/type";
|
||||||
|
|
||||||
export type AppConfig = typeof mockData extends { zhCN: infer Z } ? Z : never;
|
export type AppConfig = typeof mockData extends { "zh-CN": infer Z } ? Z : never;
|
||||||
export type I18nData = { zhCN: AppConfig; en: AppConfig };
|
export type I18nData = { "zh-CN": AppConfig; "en-US": AppConfig };
|
||||||
export type LocaleKey = "zhCN" | "en";
|
|
||||||
|
/** 根据 navigator.language 映射到支持的 LocaleKey */
|
||||||
|
function getLocaleFromNavigator(): LocaleKey {
|
||||||
|
const lang = navigator.language as LocaleKey;
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
interface StoreState {
|
interface StoreState {
|
||||||
locale: LocaleKey;
|
locale: LocaleKey;
|
||||||
i18nData: I18nData | null;
|
i18nData: I18nData | null;
|
||||||
appConfig: AppConfig | null;
|
appConfig: AppConfig | null;
|
||||||
|
supportLocales: SupportLocale[];
|
||||||
token: string | null;
|
token: string | null;
|
||||||
setLocale: (locale: LocaleKey) => void;
|
setLocale: (locale: LocaleKey) => void;
|
||||||
setAppConfig: (data: I18nData) => void;
|
setAppConfig: (data: I18nData) => void;
|
||||||
setToken: (token: string | null) => void;
|
setToken: (token: string | null) => void;
|
||||||
|
setSupportLocales: (locales: SupportLocale[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStore = create<StoreState>()(
|
export const useStore = create<StoreState>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
locale: "zhCN",
|
locale: 'zh-CN',
|
||||||
i18nData: null,
|
i18nData: null,
|
||||||
appConfig: null,
|
appConfig: null,
|
||||||
|
supportLocales: [],
|
||||||
token: null,
|
token: null,
|
||||||
setLocale: (locale) =>
|
setLocale: (locale) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
|
|
@ -32,21 +41,28 @@ export const useStore = create<StoreState>()(
|
||||||
setAppConfig: (data) =>
|
setAppConfig: (data) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
i18nData: data,
|
i18nData: data,
|
||||||
appConfig: data[state.locale] ?? null,
|
appConfig: data[state.locale] ?? data['en-US'] ?? data['zh-CN'] ?? null,
|
||||||
})),
|
})),
|
||||||
setToken: (token) => set({ token }),
|
setToken: (token) => set({ token }),
|
||||||
|
setSupportLocales: (locales: SupportLocale[]) => set({ supportLocales: locales }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "yintai-store",
|
name: "yintai-store",
|
||||||
version: 1,
|
version: 1,
|
||||||
migrate: (persisted: unknown) => {
|
migrate: (persisted: unknown) => {
|
||||||
const p = persisted as Record<string, unknown>;
|
const p = persisted as Record<string, unknown>;
|
||||||
if (p?.i18nData || !p?.appConfig) return p;
|
const validLocales: LocaleKey[] = ["zh-CN", "en-US"];
|
||||||
|
const locale = validLocales.includes(p?.locale as LocaleKey)
|
||||||
|
? (p.locale as LocaleKey)
|
||||||
|
: getLocaleFromNavigator();
|
||||||
|
if (p?.i18nData || !p?.appConfig) {
|
||||||
|
return { ...p, locale };
|
||||||
|
}
|
||||||
const legacy = p.appConfig as AppConfig;
|
const legacy = p.appConfig as AppConfig;
|
||||||
return {
|
return {
|
||||||
...p,
|
...p,
|
||||||
locale: (p.locale as LocaleKey) ?? "zhCN",
|
locale,
|
||||||
i18nData: { zhCN: legacy, en: legacy },
|
i18nData: { "zh-CN": legacy, "en-US": legacy },
|
||||||
appConfig: legacy,
|
appConfig: legacy,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* 项目类型定义集中管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
// === 导航 & 路由 ===
|
||||||
|
export type NavChild = { path: string; label: string };
|
||||||
|
export type NavItem = { path: string; label: string; children?: NavChild[]; index?: boolean };
|
||||||
|
|
||||||
|
// === 国际化 ===
|
||||||
|
export type LocaleKey = "zh-CN" | "en-US";
|
||||||
|
export type SupportLocale = { key: LocaleKey; label: string };
|
||||||
|
|
||||||
|
// === 页面配置 ===
|
||||||
|
export type BannerConfig = {
|
||||||
|
title?: string;
|
||||||
|
content?: string;
|
||||||
|
subtitle?: string;
|
||||||
|
largeContent?: string;
|
||||||
|
titleSize?: "large" | "medium" | string;
|
||||||
|
showBreadcrumb?: boolean;
|
||||||
|
backgroundImage?: string | string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// === 从 store 导出(依赖 mockData)===
|
||||||
|
export type { AppConfig, I18nData } from "@/store";
|
||||||
Loading…
Reference in New Issue