fix: anchor empty listing pages and footer
This commit is contained in:
@@ -64,6 +64,8 @@ const siteTexts = [
|
|||||||
['cases.detail_note', '案例详情提示', '原状、目标、差距、根因、介入、路径、行动、结果与迭代,构成完整的案例叙事。'],
|
['cases.detail_note', '案例详情提示', '原状、目标、差距、根因、介入、路径、行动、结果与迭代,构成完整的案例叙事。'],
|
||||||
['cases.value_placeholder', '案例关联价值提示', '暂未关联价值坐标'],
|
['cases.value_placeholder', '案例关联价值提示', '暂未关联价值坐标'],
|
||||||
['cases.service_placeholder', '案例关联服务提示', '暂未关联服务'],
|
['cases.service_placeholder', '案例关联服务提示', '暂未关联服务'],
|
||||||
|
['cases.empty_title', '案例空状态标题', '案例内容正在整理中'],
|
||||||
|
['cases.empty_description', '案例空状态说明', '如需了解与企业当前阶段匹配的实践路径,欢迎直接咨询顾问。'],
|
||||||
['contact.company_placeholder', '联系人企业选填提示', '选填'],
|
['contact.company_placeholder', '联系人企业选填提示', '选填'],
|
||||||
['contact.need_placeholder', '咨询需求输入提示', '请简述你的企业现状与期望'],
|
['contact.need_placeholder', '咨询需求输入提示', '请简述你的企业现状与期望'],
|
||||||
['contact.privacy_note', '咨询隐私说明', '提交即视为同意隐私与合规说明。'],
|
['contact.privacy_note', '咨询隐私说明', '提交即视为同意隐私与合规说明。'],
|
||||||
@@ -86,10 +88,14 @@ const siteTexts = [
|
|||||||
['services.custom_cta', '定制服务行动文字', '咨询/获取方案(无价格)'],
|
['services.custom_cta', '定制服务行动文字', '咨询/获取方案(无价格)'],
|
||||||
['services.placeholder', '服务页补充说明', '围绕企业当前阶段,选择合适的行动路径。'],
|
['services.placeholder', '服务页补充说明', '围绕企业当前阶段,选择合适的行动路径。'],
|
||||||
['services.detail_note', '服务详情补充说明', '明确交付物、周期、适合对象与服务流程,让合作预期清晰可见。'],
|
['services.detail_note', '服务详情补充说明', '明确交付物、周期、适合对象与服务流程,让合作预期清晰可见。'],
|
||||||
|
['services.empty_title', '服务空状态标题', '服务内容正在更新中'],
|
||||||
|
['services.empty_description', '服务空状态说明', '欢迎联系我们,获取适合企业当前阶段的沟通与服务建议。'],
|
||||||
['insights.header_placeholder', '洞察页补充说明', '围绕企业进化、价值坐标与经营现场,持续记录真实观察。'],
|
['insights.header_placeholder', '洞察页补充说明', '围绕企业进化、价值坐标与经营现场,持续记录真实观察。'],
|
||||||
['insights.title', '洞察页标题', '认知观察'],
|
['insights.title', '洞察页标题', '认知观察'],
|
||||||
['insights.header_description', '洞察页说明', '围绕企业进化、价值坐标、A/M/B 与方法论的持续观察与沉淀。'],
|
['insights.header_description', '洞察页说明', '围绕企业进化、价值坐标、A/M/B 与方法论的持续观察与沉淀。'],
|
||||||
['insights.detail_note', '洞察详情补充说明', '从真实经营现场出发,持续记录可验证、可复盘的观察。'],
|
['insights.detail_note', '洞察详情补充说明', '从真实经营现场出发,持续记录可验证、可复盘的观察。'],
|
||||||
|
['insights.empty_title', '洞察空状态标题', '内容正在持续沉淀'],
|
||||||
|
['insights.empty_description', '洞察空状态说明', '欢迎关注企业进化、价值坐标与经营现场的后续观察。'],
|
||||||
['selfcheck.header_note', '轻量对照页头部说明', '仅做轻量对照,深度诊断需线下陪跑。'],
|
['selfcheck.header_note', '轻量对照页头部说明', '仅做轻量对照,深度诊断需线下陪跑。'],
|
||||||
['selfcheck.storage_note', '轻量对照结果说明', '结果仅本地展示,不存储。'],
|
['selfcheck.storage_note', '轻量对照结果说明', '结果仅本地展示,不存储。'],
|
||||||
['selfcheck.result_title', '轻量对照结果标题', '对照结果'],
|
['selfcheck.result_title', '轻量对照结果标题', '对照结果'],
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import MobileStickyCTA from './MobileStickyCTA'
|
|||||||
|
|
||||||
export default function Layout() {
|
export default function Layout() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="app-shell">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<main style={{ minHeight: '60vh' }}>
|
<main className="site-main">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
<MobileStickyCTA />
|
<MobileStickyCTA />
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { CASES as FALLBACK_CASES } from '@/data/mock'
|
|
||||||
import { useCMS, useCMSTexts, getCMSCases } from '@/services/cms'
|
import { useCMS, useCMSTexts, getCMSCases } from '@/services/cms'
|
||||||
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
||||||
|
|
||||||
export default function Cases() {
|
export default function Cases() {
|
||||||
const { data: CASES } = useCMS(getCMSCases, FALLBACK_CASES)
|
const { data: CASES } = useCMS(getCMSCases, [])
|
||||||
const { data: texts } = useCMSTexts()
|
const { data: texts } = useCMSTexts()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -22,7 +21,7 @@ export default function Cases() {
|
|||||||
</div>
|
</div>
|
||||||
<section className="section">
|
<section className="section">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="grid-2">
|
{CASES.length ? <div className="grid-2">
|
||||||
{CASES.map(c => (
|
{CASES.map(c => (
|
||||||
<Link key={c.id} to={`/cases/${c.slug}`} className="card" style={{ display: 'block' }}>
|
<Link key={c.id} to={`/cases/${c.slug}`} className="card" style={{ display: 'block' }}>
|
||||||
<div style={{ fontSize: 11, color: '#0f6b5b', fontWeight: 800, letterSpacing: '0.08em' }}>CASE · {c.status.toUpperCase()}</div>
|
<div style={{ fontSize: 11, color: '#0f6b5b', fontWeight: 800, letterSpacing: '0.08em' }}>CASE · {c.status.toUpperCase()}</div>
|
||||||
@@ -31,7 +30,7 @@ export default function Cases() {
|
|||||||
<span style={{ fontSize: 12, color: '#163E70', fontWeight: 700, marginTop: 10, display: 'inline-block' }}>{texts['cases.detail_link'] || '查看完整叙事'} →</span>
|
<span style={{ fontSize: 12, color: '#163E70', fontWeight: 700, marginTop: 10, display: 'inline-block' }}>{texts['cases.detail_link'] || '查看完整叙事'} →</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div> : <div className="empty-state"><div><h2>{texts['cases.empty_title'] || '案例内容正在整理中'}</h2><p>{texts['cases.empty_description'] || '如需了解与企业当前阶段匹配的实践路径,欢迎直接咨询顾问。'}</p></div></div>}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<GlobalCTA />
|
<GlobalCTA />
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { INSIGHTS as FALLBACK_INSIGHTS } from '@/data/mock'
|
|
||||||
import { useCMS, useCMSTexts, getCMSInsights } from '@/services/cms'
|
import { useCMS, useCMSTexts, getCMSInsights } from '@/services/cms'
|
||||||
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
||||||
|
|
||||||
export default function Insights() {
|
export default function Insights() {
|
||||||
const { data: INSIGHTS } = useCMS(getCMSInsights, FALLBACK_INSIGHTS)
|
const { data: INSIGHTS } = useCMS(getCMSInsights, [])
|
||||||
const { data: texts } = useCMSTexts()
|
const { data: texts } = useCMSTexts()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -22,7 +21,7 @@ export default function Insights() {
|
|||||||
</div>
|
</div>
|
||||||
<section className="section">
|
<section className="section">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="grid-2">
|
{INSIGHTS.length ? <div className="grid-2">
|
||||||
{INSIGHTS.map(i => (
|
{INSIGHTS.map(i => (
|
||||||
<Link key={i.id} to={`/insights/${i.slug}`} className="card" style={{ display: 'block' }}>
|
<Link key={i.id} to={`/insights/${i.slug}`} className="card" style={{ display: 'block' }}>
|
||||||
<div style={{ fontSize: 11, color: '#0f6b5b', fontWeight: 800, letterSpacing: '0.08em' }}>{i.category.toUpperCase()}</div>
|
<div style={{ fontSize: 11, color: '#0f6b5b', fontWeight: 800, letterSpacing: '0.08em' }}>{i.category.toUpperCase()}</div>
|
||||||
@@ -33,7 +32,7 @@ export default function Insights() {
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div> : <div className="empty-state"><div><h2>{texts['insights.empty_title'] || '内容正在持续沉淀'}</h2><p>{texts['insights.empty_description'] || '欢迎关注企业进化、价值坐标与经营现场的后续观察。'}</p></div></div>}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<GlobalCTA />
|
<GlobalCTA />
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { SERVICES as FALLBACK_SERVICES } from '@/data/mock'
|
|
||||||
import { useCMS, useCMSTexts, getCMSServices } from '@/services/cms'
|
import { useCMS, useCMSTexts, getCMSServices } from '@/services/cms'
|
||||||
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
import GlobalCTA from '@/components/Layout/GlobalCTA'
|
||||||
|
|
||||||
export default function Services() {
|
export default function Services() {
|
||||||
const { data: SERVICES } = useCMS(getCMSServices, FALLBACK_SERVICES)
|
const { data: SERVICES } = useCMS(getCMSServices, [])
|
||||||
const { data: texts } = useCMSTexts()
|
const { data: texts } = useCMSTexts()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -22,7 +21,7 @@ export default function Services() {
|
|||||||
</div>
|
</div>
|
||||||
<section className="section">
|
<section className="section">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="grid-3">
|
{SERVICES.length ? <div className="grid-3">
|
||||||
{SERVICES.map(s => (
|
{SERVICES.map(s => (
|
||||||
<div key={s.id} className="card" style={{ display: 'flex', flexDirection: 'column' }}>
|
<div key={s.id} className="card" style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<div style={{ fontSize: 11, letterSpacing: '0.08em', color: s.serviceType === 'standard' ? '#163E70' : '#0f6b5b', fontWeight: 800 }}>
|
<div style={{ fontSize: 11, letterSpacing: '0.08em', color: s.serviceType === 'standard' ? '#163E70' : '#0f6b5b', fontWeight: 800 }}>
|
||||||
@@ -52,7 +51,7 @@ export default function Services() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div> : <div className="empty-state"><div><h2>{texts['services.empty_title'] || '服务内容正在更新中'}</h2><p>{texts['services.empty_description'] || '欢迎联系我们,获取适合企业当前阶段的沟通与服务建议。'}</p></div></div>}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<GlobalCTA />
|
<GlobalCTA />
|
||||||
|
|||||||
+21
-1
@@ -14,14 +14,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
html { scroll-behavior: smooth; }
|
html { scroll-behavior: smooth; min-height: 100%; }
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
-webkit-font-smoothing: antialiased;
|
-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 { color: var(--primary); text-decoration: none; }
|
||||||
a:hover { color: var(--primary-light); }
|
a:hover { color: var(--primary-light); }
|
||||||
img { max-width: 100%; display: block; }
|
img { max-width: 100%; display: block; }
|
||||||
@@ -66,6 +74,18 @@ img { max-width: 100%; display: block; }
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
.placeholder strong { color: #b45309; }
|
.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 */
|
/* Cards */
|
||||||
.card {
|
.card {
|
||||||
|
|||||||
Reference in New Issue
Block a user