diff --git a/cms/server/src/seed.js b/cms/server/src/seed.js index 64df1ad..407384b 100644 --- a/cms/server/src/seed.js +++ b/cms/server/src/seed.js @@ -64,6 +64,8 @@ const siteTexts = [ ['cases.detail_note', '案例详情提示', '原状、目标、差距、根因、介入、路径、行动、结果与迭代,构成完整的案例叙事。'], ['cases.value_placeholder', '案例关联价值提示', '暂未关联价值坐标'], ['cases.service_placeholder', '案例关联服务提示', '暂未关联服务'], + ['cases.empty_title', '案例空状态标题', '案例内容正在整理中'], + ['cases.empty_description', '案例空状态说明', '如需了解与企业当前阶段匹配的实践路径,欢迎直接咨询顾问。'], ['contact.company_placeholder', '联系人企业选填提示', '选填'], ['contact.need_placeholder', '咨询需求输入提示', '请简述你的企业现状与期望'], ['contact.privacy_note', '咨询隐私说明', '提交即视为同意隐私与合规说明。'], @@ -86,10 +88,14 @@ const siteTexts = [ ['services.custom_cta', '定制服务行动文字', '咨询/获取方案(无价格)'], ['services.placeholder', '服务页补充说明', '围绕企业当前阶段,选择合适的行动路径。'], ['services.detail_note', '服务详情补充说明', '明确交付物、周期、适合对象与服务流程,让合作预期清晰可见。'], + ['services.empty_title', '服务空状态标题', '服务内容正在更新中'], + ['services.empty_description', '服务空状态说明', '欢迎联系我们,获取适合企业当前阶段的沟通与服务建议。'], ['insights.header_placeholder', '洞察页补充说明', '围绕企业进化、价值坐标与经营现场,持续记录真实观察。'], ['insights.title', '洞察页标题', '认知观察'], ['insights.header_description', '洞察页说明', '围绕企业进化、价值坐标、A/M/B 与方法论的持续观察与沉淀。'], ['insights.detail_note', '洞察详情补充说明', '从真实经营现场出发,持续记录可验证、可复盘的观察。'], + ['insights.empty_title', '洞察空状态标题', '内容正在持续沉淀'], + ['insights.empty_description', '洞察空状态说明', '欢迎关注企业进化、价值坐标与经营现场的后续观察。'], ['selfcheck.header_note', '轻量对照页头部说明', '仅做轻量对照,深度诊断需线下陪跑。'], ['selfcheck.storage_note', '轻量对照结果说明', '结果仅本地展示,不存储。'], ['selfcheck.result_title', '轻量对照结果标题', '对照结果'], diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index f1bbb28..f472094 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -5,13 +5,13 @@ import MobileStickyCTA from './MobileStickyCTA' export default function Layout() { return ( - <> +
-
+
) } diff --git a/src/pages/Cases/index.tsx b/src/pages/Cases/index.tsx index aec2cc8..ec6f7de 100644 --- a/src/pages/Cases/index.tsx +++ b/src/pages/Cases/index.tsx @@ -1,10 +1,9 @@ import { Link } from 'react-router-dom' -import { CASES as FALLBACK_CASES } from '@/data/mock' import { useCMS, useCMSTexts, getCMSCases } from '@/services/cms' import GlobalCTA from '@/components/Layout/GlobalCTA' export default function Cases() { - const { data: CASES } = useCMS(getCMSCases, FALLBACK_CASES) + const { data: CASES } = useCMS(getCMSCases, []) const { data: texts } = useCMSTexts() return (
@@ -22,7 +21,7 @@ export default function Cases() {
-
+ {CASES.length ?
{CASES.map(c => (
CASE · {c.status.toUpperCase()}
@@ -31,7 +30,7 @@ export default function Cases() { {texts['cases.detail_link'] || '查看完整叙事'} → ))} -
+
:

{texts['cases.empty_title'] || '案例内容正在整理中'}

{texts['cases.empty_description'] || '如需了解与企业当前阶段匹配的实践路径,欢迎直接咨询顾问。'}

}
diff --git a/src/pages/Insights/index.tsx b/src/pages/Insights/index.tsx index a28a4c2..3fd9189 100644 --- a/src/pages/Insights/index.tsx +++ b/src/pages/Insights/index.tsx @@ -1,10 +1,9 @@ import { Link } from 'react-router-dom' -import { INSIGHTS as FALLBACK_INSIGHTS } from '@/data/mock' import { useCMS, useCMSTexts, getCMSInsights } from '@/services/cms' import GlobalCTA from '@/components/Layout/GlobalCTA' export default function Insights() { - const { data: INSIGHTS } = useCMS(getCMSInsights, FALLBACK_INSIGHTS) + const { data: INSIGHTS } = useCMS(getCMSInsights, []) const { data: texts } = useCMSTexts() return (
@@ -22,7 +21,7 @@ export default function Insights() {
-
+ {INSIGHTS.length ?
{INSIGHTS.map(i => (
{i.category.toUpperCase()}
@@ -33,7 +32,7 @@ export default function Insights() {
))} -
+
:

{texts['insights.empty_title'] || '内容正在持续沉淀'}

{texts['insights.empty_description'] || '欢迎关注企业进化、价值坐标与经营现场的后续观察。'}

}
diff --git a/src/pages/Services/index.tsx b/src/pages/Services/index.tsx index c2f28f1..dbfcaa9 100644 --- a/src/pages/Services/index.tsx +++ b/src/pages/Services/index.tsx @@ -1,10 +1,9 @@ import { Link } from 'react-router-dom' -import { SERVICES as FALLBACK_SERVICES } from '@/data/mock' import { useCMS, useCMSTexts, getCMSServices } from '@/services/cms' import GlobalCTA from '@/components/Layout/GlobalCTA' export default function Services() { - const { data: SERVICES } = useCMS(getCMSServices, FALLBACK_SERVICES) + const { data: SERVICES } = useCMS(getCMSServices, []) const { data: texts } = useCMSTexts() return (
@@ -22,7 +21,7 @@ export default function Services() {
-
+ {SERVICES.length ?
{SERVICES.map(s => (
@@ -52,7 +51,7 @@ export default function Services() {
))} -
+
:

{texts['services.empty_title'] || '服务内容正在更新中'}

{texts['services.empty_description'] || '欢迎联系我们,获取适合企业当前阶段的沟通与服务建议。'}

}
diff --git a/src/styles/global.css b/src/styles/global.css index 5be1689..2637bf3 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -14,14 +14,22 @@ } * { box-sizing: border-box; margin: 0; padding: 0; } -html { scroll-behavior: smooth; } +html { scroll-behavior: smooth; min-height: 100%; } body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; color: var(--text); background: var(--bg); line-height: 1.7; -webkit-font-smoothing: antialiased; + min-height: 100vh; } +#root { min-height: 100vh; } +.app-shell { min-height: 100vh; display: flex; flex-direction: column; } +.site-main { flex: 1; display: flex; flex-direction: column; } +/* Each route is a page-level wrapper. Let a short page fill the available space, + then anchor its final CTA immediately above the footer. */ +.site-main > * { flex: 1; display: flex; flex-direction: column; } +.site-main > * > .cta-band:last-child { margin-top: auto; } a { color: var(--primary); text-decoration: none; } a:hover { color: var(--primary-light); } img { max-width: 100%; display: block; } @@ -66,6 +74,18 @@ img { max-width: 100%; display: block; } line-height: 1.6; } .placeholder strong { color: #b45309; } +.empty-state { + min-height: 180px; + display: grid; + place-items: center; + text-align: center; + background: var(--bg-white); + border: 1px dashed var(--border); + border-radius: 12px; + padding: 32px 24px; +} +.empty-state h2 { color: var(--primary); font-size: 18px; margin-bottom: 8px; } +.empty-state p { color: var(--text-secondary); font-size: 14px; max-width: 480px; } /* Cards */ .card {