diff --git a/src/App.tsx b/src/App.tsx
index ed5b500..8af0149 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,7 +4,8 @@ import "./App.css"
import routes from "@/Routes";
import { useCallback, useEffect, useState } from "react";
import appApi from "@/api/app";
-import { useStore, type I18nData } from "@/store";
+import { useStore } from "@/store";
+import type { I18nData, SupportLocale } from "@/type";
function AppRoutes() {
return useRoutes(routes);
@@ -36,7 +37,12 @@ function App() {
const data = res.data as I18nData;
useStore.getState().setAppConfig(data);
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);
} catch (error) {
console.log(error);
diff --git a/src/api/mockData.ts b/src/api/mockData.ts
index b13a80a..c1f2ba5 100644
--- a/src/api/mockData.ts
+++ b/src/api/mockData.ts
@@ -1,14 +1,12 @@
/**
* 接口返回数据结构
* mockData: {
- * zhCN: {...},
- * en: {...}
+ * "zh-CN": {...},
+ * "en-US": {...}
* }
*/
-export type NavChild = { path: string; label: string };
-export type NavItem = { path: string; label: string; children?: NavChild[]; index?: boolean };
-export type LocaleKey = "zhCN" | "en";
+import type { NavChild, NavItem } from "@/type";
const zhCN = {
companyName: "银泰",
@@ -72,10 +70,6 @@ const zhCN = {
],
},
] as NavItem[],
- langMenuItems: [
- { key: "zhCN", label: "中文" },
- { key: "en", label: "English" },
- ],
},
footer: {
@@ -309,7 +303,44 @@ const zhCN = {
backgroundImage: '',
sideImage: "/images/bg-invest-group.png",
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[],
- langMenuItems: [
- { key: "zhCN", label: "中文" },
- { key: "en", label: "English" },
- ],
},
footer: {
...zhCN.footer,
@@ -1002,5 +1029,5 @@ const en: typeof zhCN = {
};
export type LocaleConfig = typeof zhCN;
-const mockData = { zhCN, en };
+const mockData = { "zh-CN": zhCN, "en-US": en };
export default mockData;
diff --git a/src/components/banner.tsx b/src/components/banner.tsx
index 3d0441e..290fa84 100644
--- a/src/components/banner.tsx
+++ b/src/components/banner.tsx
@@ -6,19 +6,11 @@ import "swiper/css/effect-fade";
import styles from "./Banner.module.css";
import { useMemo } from "react";
import { useStore } from "@/store";
+import type { BannerConfig } from "@/type";
const FALLBACK_GRADIENT = "linear-gradient(135deg, #1a2a4a 0%, #2d4a7c 100%)";
-
-export type BannerConfig = {
- title?: string;
- content?: string;
- subtitle?: string;
- largeContent?: string;
- titleSize?: "large" | "medium" | string;
- showBreadcrumb?: boolean;
- backgroundImage?: string | string[];
-};
+export type { BannerConfig } from "@/type";
type Props = {
title: string;
diff --git a/src/components/layout/RowAccordion/index.module.css b/src/components/layout/RowAccordion/index.module.css
index 5dad5fa..f746635 100644
--- a/src/components/layout/RowAccordion/index.module.css
+++ b/src/components/layout/RowAccordion/index.module.css
@@ -86,6 +86,7 @@
margin-bottom: 6px;
display: flex;
align-items: center;
+ white-space: nowrap;
}
.contentItemSubtitle {
font-size: 20px;
@@ -93,6 +94,7 @@
}
.contentItemContentWrapper {
+ box-sizing: border-box;
height: 0;
opacity: 0;
transition: all var(--duration) ease-in-out;
@@ -100,11 +102,12 @@
display: flex;
flex-direction: column;
justify-content: space-between;
- padding: 60px 0;
+ /* padding: 60px 0; */
+ margin-top: 20px;
}
.contentItemContent {
font-size: 16px;
- margin-top: 40px;
+ /* margin-top: 40px; */
}
.contentItemLinks {
diff --git a/src/layouts/Footer.tsx b/src/layouts/Footer.tsx
index d7f2f10..bbf9199 100644
--- a/src/layouts/Footer.tsx
+++ b/src/layouts/Footer.tsx
@@ -73,7 +73,7 @@ function FooterLower({
{lowerLinks.map((link, index) => (
< >
-
+
{link.label}
{index !== lowerLinks.length - 1 &&
·}
diff --git a/src/layouts/Header.module.css b/src/layouts/Header.module.css
index 360de75..f94de8d 100644
--- a/src/layouts/Header.module.css
+++ b/src/layouts/Header.module.css
@@ -153,6 +153,7 @@
display: inline-flex;
flex-direction: column;
text-align: center;
+ transform: translateX(-50%);
}
.dropPanelLink {
diff --git a/src/layouts/Header.tsx b/src/layouts/Header.tsx
index 82b095a..ae9017e 100644
--- a/src/layouts/Header.tsx
+++ b/src/layouts/Header.tsx
@@ -3,29 +3,29 @@ import { Dropdown } from "antd";
import styles from "./Header.module.css";
import { useEffect, useMemo, useState } from "react";
import { useStore } from "@/store";
-import type { NavChild } from "@/api/mockData";
-import type { LocaleKey } from "@/store";
+import type { NavChild, LocaleKey, SupportLocale } from "@/type";
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() {
const location = useLocation();
const appConfig = useStore((s) => s.appConfig);
const locale = useStore((s) => s.locale);
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 langMenuItems = appConfig?.header?.langMenuItems ?? DEFAULT_LANG_ITEMS;
+ const langMenuItems: SupportLocale[] = supportLocales || [];
const logo = appConfig?.logo ?? "/images/logo.png";
const [activeNav, setActiveNav] = useState("");
const [showDropPanel, setShowDropPanel] = useState(false);
const [hoverElLeft, setHoverElLeft] = useState(0);
const handleNavEnter = (e: any, path: string) => {
- // left + 元素宽度的一半
const left = e.target.offsetLeft;
- setHoverElLeft(left);
+ // 计算元素宽度
+ const width = e.target.offsetWidth;
+ setHoverElLeft(left + width / 2);
setActiveNav(path);
setShowDropPanel(true);
}
@@ -87,9 +87,9 @@ export default function Header() {
({
+ items: langMenuItems.map((item: SupportLocale) => ({
...item,
- onClick: () => setLocale(item.key as LocaleKey),
+ onClick: () => setLocale(item.key),
})),
}}
placement="bottomRight"
@@ -97,7 +97,7 @@ export default function Header() {
>
diff --git a/src/pages/About/Founder.module.css b/src/pages/About/Founder.module.css
index f9a1885..51a696a 100644
--- a/src/pages/About/Founder.module.css
+++ b/src/pages/About/Founder.module.css
@@ -1,5 +1,5 @@
.section {
- background: rgba(255,255,255,0.6);
+ background: rgba(255, 255, 255, 0.6);
border-radius: 0;
min-height: 100vh;
width: 100%;
@@ -26,38 +26,45 @@
aspect-ratio: 680 / 800;
}
}
-.images img{
+
+.images img {
width: 100%;
aspect-ratio: 680 / 800;
- background: rgba(0,0,0,0.3);
+ background: rgba(0, 0, 0, 0.3);
object-fit: cover;
}
.images .imageItem {
position: relative;
overflow: hidden;
+
.imageOverlay {
background: transparent;
}
}
+
.images .imageItem:hover .imageOverlay {
top: 0;
- background: rgba(20,53,92,0.8);
+ background: rgba(20, 53, 92, 0.8);
+
.imageOverlayDesc {
opacity: 1;
}
+
.imageOverlayTitle span {
border-bottom: 3px solid #FFFFFF;
}
}
+
.images .imageMask {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
- background: rgba(0,0,0,0.3);
+ background: rgba(0, 0, 0, 0.3);
}
+
.images .imageOverlay {
position: absolute;
top: 78%;
@@ -67,33 +74,38 @@
color: #fff;
padding: 3.75rem 2.5rem;
transition: top 0.3s ease-in-out;
+
.imageOverlayDesc {
opacity: 0;
}
}
+
.images .imageOverlayTitle {
font-size: 1.5rem;
font-weight: 500;
height: 4.375rem;
color: #fff;
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 {
display: inline-block;
height: 4.4375rem;
border-bottom: 3px solid #14355C;
}
+
.images .imageOverlayDesc {
font-size: 1rem;
color: #fff;
margin-top: 3.125rem;
}
-.sectionFounder{
+.sectionFounder {
padding: 6.25rem 0 9.375rem;
min-height: 100vh;
}
+
.founderIntroduction h2 {
font-size: 2.5rem;
font-weight: 700;
@@ -101,6 +113,7 @@
margin-bottom: 3.125rem;
text-align: center;
}
+
.founderIntroduction p {
font-size: 1.125rem;
line-height: 1.5;
@@ -109,22 +122,25 @@
letter-spacing: 0.05em;
text-align: center;
}
-.founderPhoto{
+
+.founderPhoto {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6.25rem;
margin-top: 6.25rem;
}
-.founderPhoto img{
+.founderPhoto img {
flex: 1;
width: 100%;
aspect-ratio: 680 / 800;
}
-.founderPhotoContent{
+
+.founderPhotoContent {
padding-top: 6.25rem;
padding-right: 6.25rem;
}
+
.founderPhotoContent p {
font-size: 1rem;
line-height: 1.5;
@@ -133,6 +149,28 @@
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) {
.section {
padding: 2rem 1rem;
diff --git a/src/pages/About/Founder.tsx b/src/pages/About/Founder.tsx
index 91c2a9c..0bd1b3f 100644
--- a/src/pages/About/Founder.tsx
+++ b/src/pages/About/Founder.tsx
@@ -2,6 +2,7 @@ import Banner from "@/components/Banner";
import styles from "./Founder.module.css";
import ParagraphSection from "@/components/layout/ParagraphSection";
import { useStore } from "@/store";
+import Section from "@/components/layout/Section";
export default function AboutFounder() {
const appConfig = useStore((s) => s.appConfig);
@@ -10,6 +11,8 @@ export default function AboutFounder() {
const banner = founder?.banner;
const section1Data = founder?.section1Data;
const section2Data = founder?.section2Data;
+ const section3Data = founder?.section3Data;
+ const section4Data = founder?.section4Data;
if (!founder) return null;
@@ -63,6 +66,26 @@ export default function AboutFounder() {
)}
+
+ {/* 社会职务 */}
+
+
+ {Array.from({
+ length: Math.max(0, ...(section3Data?.columns?.map((c) => c.length) ?? [0])),
+ }).flatMap((_, rowIndex) =>
+ (section3Data?.columns ?? []).map((colItems, colIndex) => (
+
+ {colItems[rowIndex] ?
{colItems[rowIndex].title} : null}
+
+ ))
+ )}
+
+
+
+ {/* 荣誉奖项 */}
+
);
}
diff --git a/src/pages/Business/CommercialGroup.tsx b/src/pages/Business/CommercialGroup.tsx
index 7b362d9..8fc1353 100644
--- a/src/pages/Business/CommercialGroup.tsx
+++ b/src/pages/Business/CommercialGroup.tsx
@@ -60,7 +60,7 @@ export default function BusinessCommercialGroup() {
) : (
setIn77ImgError(true)}
style={{ width: "100%", height: "800px" }}
/>
diff --git a/src/store/index.ts b/src/store/index.ts
index 98855b7..abfa81b 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -2,27 +2,36 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
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 I18nData = { zhCN: AppConfig; en: AppConfig };
-export type LocaleKey = "zhCN" | "en";
+export type AppConfig = typeof mockData extends { "zh-CN": infer Z } ? Z : never;
+export type I18nData = { "zh-CN": AppConfig; "en-US": AppConfig };
+
+/** 根据 navigator.language 映射到支持的 LocaleKey */
+function getLocaleFromNavigator(): LocaleKey {
+ const lang = navigator.language as LocaleKey;
+ return lang;
+}
interface StoreState {
locale: LocaleKey;
i18nData: I18nData | null;
appConfig: AppConfig | null;
+ supportLocales: SupportLocale[];
token: string | null;
setLocale: (locale: LocaleKey) => void;
setAppConfig: (data: I18nData) => void;
setToken: (token: string | null) => void;
+ setSupportLocales: (locales: SupportLocale[]) => void;
}
export const useStore = create()(
persist(
(set) => ({
- locale: "zhCN",
+ locale: 'zh-CN',
i18nData: null,
appConfig: null,
+ supportLocales: [],
token: null,
setLocale: (locale) =>
set((state) => ({
@@ -32,21 +41,28 @@ export const useStore = create()(
setAppConfig: (data) =>
set((state) => ({
i18nData: data,
- appConfig: data[state.locale] ?? null,
+ appConfig: data[state.locale] ?? data['en-US'] ?? data['zh-CN'] ?? null,
})),
setToken: (token) => set({ token }),
+ setSupportLocales: (locales: SupportLocale[]) => set({ supportLocales: locales }),
}),
{
name: "yintai-store",
version: 1,
migrate: (persisted: unknown) => {
const p = persisted as Record;
- 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;
return {
...p,
- locale: (p.locale as LocaleKey) ?? "zhCN",
- i18nData: { zhCN: legacy, en: legacy },
+ locale,
+ i18nData: { "zh-CN": legacy, "en-US": legacy },
appConfig: legacy,
};
},
diff --git a/src/type/index.ts b/src/type/index.ts
new file mode 100644
index 0000000..49859d2
--- /dev/null
+++ b/src/type/index.ts
@@ -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";