Compare commits

...

3 Commits

Author SHA1 Message Date
47ed858bb1 去掉看经济标题
Some checks failed
Docker Build and Push / build-image (push) Failing after 33m43s
2026-04-06 09:13:07 +08:00
620e46661f 丰富内容 2026-04-06 09:11:22 +08:00
a2bec07322 修改 ui 文字 2026-04-06 08:31:59 +08:00
16 changed files with 562 additions and 10 deletions

View File

@ -18,4 +18,4 @@ jobs:
run: docker rm -vf iboard || true run: docker rm -vf iboard || true
- name: Run container - name: Run container
run: docker run -d --name iboard -p 3000:3000 iboard run: docker run -d --name iboard -p 3080:3000 iboard

View File

@ -2,7 +2,7 @@ const Footer = () => {
return ( return (
<footer className="footer"> <footer className="footer">
<div className="footer-content"> <div className="footer-content">
<p>© 2026 iBoard. 收集和整理经济数据经济理论的形式重新组织</p> <p>© 2026 iBoard. 收集和整理经济数据策略为主体重新组织</p>
</div> </div>
</footer> </footer>
) )

View File

@ -8,8 +8,8 @@ const Navbar = () => {
</div> </div>
<div className="nav-links"> <div className="nav-links">
<Link href="/">首页</Link> <Link href="/">首页</Link>
<Link href="/data">数据</Link> <Link href="/data">经济数据</Link>
<Link href="/strategy">策略</Link> <Link href="/strategy">策略研究</Link>
</div> </div>
</nav> </nav>
) )

View File

@ -0,0 +1,17 @@
---
title: GDP增长分析与展望
---
GDP是衡量一个国家经济规模和增长速度的重要指标。本文将分析当前全球主要经济体的GDP增长情况探讨影响GDP增长的因素并对未来经济走势进行展望。
首先我们来看一下全球GDP增长的整体情况。根据国际货币基金组织(IMF)的最新数据全球GDP增长率预计将在2026年达到3.5%左右较2025年有所提升。
美国作为全球最大的经济体其GDP增长率预计将保持在2.5%左右。消费、投资和出口是美国经济增长的主要驱动力。美联储的货币政策、政府的财政政策以及地缘政治风险等因素将影响美国经济的表现。
中国的GDP增长率预计将在5.0%左右。中国经济正在从投资驱动型向消费驱动型转变,科技创新、绿色发展等新动能正在成为经济增长的重要支撑。
欧元区的GDP增长率预计将在1.8%左右。欧元区经济面临着人口老龄化、结构性改革缓慢等挑战,但也受益于欧洲央行的宽松货币政策和复苏基金的支持。
影响GDP增长的因素是多方面的包括货币政策、财政政策、技术进步、人口结构、地缘政治等。在当前全球经济不确定性增加的背景下各国需要采取适当的政策措施来促进经济增长。
展望未来,全球经济增长有望保持稳定,但仍面临着诸多挑战。科技创新、数字经济、绿色转型等将成为推动经济增长的重要力量。同时,各国需要加强国际合作,共同应对全球性挑战,如气候变化、贸易保护主义等。

View File

@ -0,0 +1,19 @@
---
title: 消费趋势的变化与商业机会
---
消费趋势的变化正在重塑商业格局。本文将分析当前消费市场的主要趋势,探讨这些变化对企业的影响,并为企业提供相应的战略建议。
首先,我们来看一下消费市场的整体情况。随着经济的发展和居民收入水平的提高,消费结构正在从生存型消费向发展型和享受型消费转变。
数字化消费已经成为主流。电子商务、移动支付、在线娱乐等数字消费方式正在改变人们的消费习惯。消费者越来越倾向于通过互联网和移动设备进行购物、娱乐和社交。
健康消费成为新的增长点。消费者对健康食品、健身服务、医疗保健等健康相关产品和服务的需求不断增加。这一趋势在疫情后更加明显,人们对健康的重视程度提高。
可持续消费理念逐渐深入人心。越来越多的消费者开始关注产品的环保性能愿意为可持续产品支付溢价。企业的ESG表现也成为消费者选择品牌的重要因素。
个性化消费需求日益凸显。消费者希望获得个性化的产品和服务,而不是千篇一律的标准化产品。企业需要通过大数据、人工智能等技术来满足消费者的个性化需求。
这些消费趋势的变化对企业提出了新的挑战和机遇。企业需要调整产品和服务策略,以适应消费者需求的变化。同时,企业也需要利用数字技术来提升运营效率,改善客户体验。
对于投资者而言,消费趋势的变化也意味着投资机会的转移。需要关注那些能够适应消费趋势变化、具有创新能力的企业。

View File

@ -0,0 +1,19 @@
---
title: 科技创新与经济增长
---
科技创新是推动经济增长的重要动力。本文将分析当前科技创新的趋势,探讨其对经济增长的影响,并为政策制定者和投资者提供参考。
首先,我们来看一下当前科技创新的主要领域。人工智能、区块链、生物技术、可再生能源等领域的技术突破正在改变我们的生活和工作方式。
人工智能技术的快速发展正在各个行业产生深远影响。从自动驾驶到医疗诊断从金融服务到制造业AI正在提高效率创造新的商业模式。
区块链技术的应用也在不断扩展。除了加密货币,区块链在供应链管理、数字身份、金融服务等领域的应用正在逐步落地。
生物技术的进步为医疗健康、农业等领域带来了新的机遇。基因编辑、精准医疗、合成生物学等技术的发展有望解决许多传统医学难以解决的问题。
可再生能源技术的突破正在加速能源转型。太阳能、风能、储能技术的成本不断下降,为实现碳中和目标提供了技术支持。
这些科技创新对经济增长的影响是多方面的。它们不仅提高了生产效率,还创造了新的产业和就业机会。同时,科技创新也带来了一些挑战,如就业结构的变化、隐私保护等问题。
对于政策制定者而言,需要制定有利于科技创新的政策环境,包括加大研发投入、培养创新人才、完善知识产权保护等。对于投资者而言,需要关注科技创新的趋势,寻找具有增长潜力的投资机会。

View File

@ -0,0 +1,15 @@
---
title: 全球贸易格局的变化与影响
---
全球贸易格局正在经历深刻的变化。本文将分析当前全球贸易的现状,探讨主要经济体的贸易政策,以及这些变化对全球经济的影响。
首先,我们来看一下全球贸易的整体情况。尽管贸易保护主义有所抬头,但全球贸易总额仍然保持增长。新兴经济体在全球贸易中的比重不断提高,正在改变传统的贸易格局。
美国、欧盟、中国等主要经济体的贸易政策对全球贸易产生重要影响。美国的贸易保护主义措施、欧盟的绿色贸易政策、中国的扩大开放政策等,都在塑造着新的贸易规则。
全球供应链的重构也是当前贸易格局变化的重要特征。出于安全和效率的考虑,许多企业正在调整供应链布局,推动区域化和多元化。
这些变化对全球经济产生了深远的影响。一方面,贸易格局的调整可能会导致短期的经济波动;另一方面,新的贸易规则和供应链布局也为经济发展带来了新的机遇。
对于企业和投资者而言,需要密切关注贸易格局的变化,调整战略以适应新的环境。同时,政府也需要制定相应的政策,促进贸易的健康发展。

View File

@ -0,0 +1,13 @@
---
title: 利率走势分析与投资策略
---
近期全球主要经济体的利率走势引起了市场的广泛关注。本文将分析当前利率环境,探讨未来可能的变化趋势,并为投资者提供相应的投资策略建议。
首先,我们来看一下美联储的货币政策走向。随着通胀压力的缓解,美联储已经开始考虑降息,但步伐可能会比较谨慎。欧洲央行和日本央行也在调整各自的货币政策,以适应经济形势的变化。
在中国央行通过调整政策利率和LPR贷款市场报价利率来引导市场利率水平。近期LPR的下调反映了货币政策的宽松取向这将有利于降低企业融资成本刺激经济增长。
对于债券投资者而言,利率下行意味着债券价格上涨,这为债券投资提供了机会。但需要注意的是,不同期限的债券对利率变化的敏感度不同,投资者需要根据自己的风险承受能力选择合适的债券品种。
对于股票投资者而言,利率下行通常有利于估值提升,特别是对成长型股票更为有利。但同时也需要关注经济基本面的变化,选择具有良好成长性和估值合理的股票。

View File

@ -0,0 +1,13 @@
---
title: 2026年中国经济展望
---
2026年中国经济将面临诸多挑战与机遇。随着全球经济格局的变化中国经济正在从高速增长向高质量发展转型。本文将分析当前经济形势预测未来趋势并提出相应的政策建议。
首先我们来看一下当前的宏观经济指标。GDP增长率、通胀水平、失业率等关键指标都显示中国经济正在稳步复苏。然而外部环境的不确定性仍然存在需要密切关注。
在产业结构方面,数字经济、绿色经济等新兴产业正在成为经济增长的新动力。传统产业的转型升级也在加速,这将为经济可持续发展注入新的活力。
对于投资者而言2026年的投资策略需要更加注重长期价值关注科技创新、消费升级等领域的投资机会。同时风险管理也变得尤为重要需要建立多元化的投资组合。
总的来说2026年中国经济有望保持稳健增长但需要应对来自内外部的各种挑战。通过深化改革、扩大开放中国经济将迎来新的发展机遇。

122
package-lock.json generated
View File

@ -9,6 +9,8 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"gray-matter": "^4.0.3",
"marked": "^17.0.6",
"next": "^14.2.3", "next": "^14.2.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -298,6 +300,15 @@
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"license": "MIT",
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/busboy": { "node_modules/busboy": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@ -481,18 +492,58 @@
"benchmarks" "benchmarks"
] ]
}, },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/eventemitter3": { "node_modules/eventemitter3": {
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
"license": "MIT",
"dependencies": {
"is-extendable": "^0.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.11", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/gray-matter": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
"integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
"license": "MIT",
"dependencies": {
"js-yaml": "^3.13.1",
"kind-of": "^6.0.2",
"section-matter": "^1.0.0",
"strip-bom-string": "^1.0.0"
},
"engines": {
"node": ">=6.0"
}
},
"node_modules/immer": { "node_modules/immer": {
"version": "10.2.0", "version": "10.2.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
@ -512,12 +563,43 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": {
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loose-envify": { "node_modules/loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -530,6 +612,18 @@
"loose-envify": "cli.js" "loose-envify": "cli.js"
} }
}, },
"node_modules/marked": {
"version": "17.0.6",
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz",
"integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.11", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@ -747,6 +841,19 @@
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
} }
}, },
"node_modules/section-matter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
"license": "MIT",
"dependencies": {
"extend-shallow": "^2.0.1",
"kind-of": "^6.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -756,6 +863,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause"
},
"node_modules/streamsearch": { "node_modules/streamsearch": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@ -764,6 +877,15 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/strip-bom-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
"integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/styled-jsx": { "node_modules/styled-jsx": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",

View File

@ -8,6 +8,8 @@
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"dependencies": { "dependencies": {
"gray-matter": "^4.0.3",
"marked": "^17.0.6",
"next": "^14.2.3", "next": "^14.2.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

71
pages/articles/[slug].js Normal file
View File

@ -0,0 +1,71 @@
import Head from 'next/head'
import Navbar from '../../components/Navbar'
import Footer from '../../components/Footer'
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { marked } from 'marked'
export default function Article({ article }) {
return (
<div className="container">
<Head>
<title>{article.title} - iBoard</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Navbar />
<main>
<div className="article-detail">
<h1 className="article-detail-title">{article.title}</h1>
<div className="article-detail-date">{article.date}</div>
<div className="article-detail-content" dangerouslySetInnerHTML={{ __html: article.contentHtml }} />
</div>
</main>
<Footer />
</div>
)
}
export async function getStaticPaths() {
const articlesDirectory = path.join(process.cwd(), 'content', 'articles')
const fileNames = fs.readdirSync(articlesDirectory)
const paths = fileNames
.filter(fileName => fileName.endsWith('.md'))
.map(fileName => ({
params: { slug: fileName.replace('.md', '') }
}))
return {
paths,
fallback: false
}
}
export async function getStaticProps({ params }) {
const articlesDirectory = path.join(process.cwd(), 'content', 'articles')
const fullPath = path.join(articlesDirectory, `${params.slug}.md`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
// 从文件名中提取日期
const dateMatch = params.slug.match(/^(\d{4}-\d{2}-\d{2})-/)
const date = dateMatch ? dateMatch[1] : ''
// 将markdown转换为HTML
const contentHtml = marked(content)
return {
props: {
article: {
id: params.slug,
title: data.title || params.slug.replace(/\d{4}-\d{2}-\d{2}-/, ''),
date,
contentHtml
}
}
}
}

View File

@ -1,8 +1,10 @@
import Head from 'next/head' import Head from 'next/head'
import Link from 'next/link'
import Navbar from '../components/Navbar' import Navbar from '../components/Navbar'
import Footer from '../components/Footer' import Footer from '../components/Footer'
import { getPagedArticles } from '../utils/articleUtils'
export default function Home() { export default function Home({ articles, totalPages, currentPage }) {
return ( return (
<div className="container"> <div className="container">
<Head> <Head>
@ -18,28 +20,71 @@ export default function Home() {
</h1> </h1>
<p className="description"> <p className="description">
收集和整理经济数据经济理论的形式重新组织 收集和整理经济数据策略为主体重新组织
</p> </p>
<div className="grid"> <div className="grid">
<div className="card"> <div className="card">
<a href="#articles-section">
<h2>经济文章</h2> <h2>经济文章</h2>
</a>
<p>最新的经济分析和研究文章</p> <p>最新的经济分析和研究文章</p>
</div> </div>
<div className="card"> <div className="card">
<h2>数据分析</h2> <Link href="/data">
<h2>经济数据</h2>
</Link>
<p>深入分析经济数据和趋势</p> <p>深入分析经济数据和趋势</p>
</div> </div>
<div className="card"> <div className="card">
<h2>经济理论</h2> <Link href="/strategy">
<h2>策略研究</h2>
</Link>
<p>基于经济理论的数据分析框架</p> <p>基于经济理论的数据分析框架</p>
</div> </div>
</div> </div>
<section id="articles-section" className="articles-section">
<div className="articles-grid">
{articles.map((article) => (
<Link key={article.id} href={`/articles/${article.id}`} className="article-card">
<div className="article-date">{article.date}</div>
<h3 className="article-title">{article.title}</h3>
<p className="article-excerpt">{article.excerpt}</p>
</Link>
))}
</div>
{totalPages > 1 && (
<div className="pagination">
{Array.from({ length: totalPages }, (_, index) => (
<Link key={index + 1} href={`/?page=${index + 1}`} className={`page-link ${currentPage === index + 1 ? 'active' : ''}`}>
{index + 1}
</Link>
))}
</div>
)}
</section>
</main> </main>
<Footer /> <Footer />
</div> </div>
) )
} }
export async function getServerSideProps({ query }) {
const page = parseInt(query.page) || 1
const { articles, total, totalPages } = getPagedArticles(page, 5)
return {
props: {
articles,
total,
totalPages,
currentPage: page
}
}
}

3
readme
View File

@ -1,5 +1,6 @@
# iBoard # iBoard
用于收集和整理经济数据,但是以经济理论的形式重新组织。 用于收集和整理经济数据,但是以策略为主体重新组织。
- 首页主要是放经济文章 - 首页主要是放经济文章
- 数据主要是放各种经济数据和图表。 - 数据主要是放各种经济数据和图表。
- 策略是放按照经济理论组织的相关数据和图表的对比。 - 策略是放按照经济理论组织的相关数据和图表的对比。

View File

@ -56,6 +56,12 @@ body {
background-color: #555; background-color: #555;
} }
/* Global link styles */
a {
text-decoration: none;
cursor: pointer;
}
/* Main content */ /* Main content */
main { main {
flex: 1; flex: 1;
@ -232,6 +238,151 @@ main {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
/* Articles section */
.articles-section {
margin-top: 4rem;
margin-bottom: 2rem;
}
.section-title {
font-size: 2rem;
color: #333;
margin-bottom: 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #ddd;
}
.articles-grid {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
}
.article-card {
background-color: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
cursor: pointer;
}
.article-card:hover {
background-color: #f9f9f9;
transform: translateX(5px);
}
.article-date {
font-size: 0.9rem;
color: #666;
margin-bottom: 0.75rem;
}
.article-title {
font-size: 1.2rem;
font-weight: 600;
color: #333;
margin-bottom: 0.75rem;
line-height: 1.4;
}
.article-excerpt {
color: #666;
line-height: 1.5;
font-size: 0.95rem;
}
/* Pagination */
.pagination {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 2rem;
}
.page-link {
padding: 0.5rem 1rem;
background-color: white;
border: 1px solid #ddd;
border-radius: 4px;
color: #333;
text-decoration: none;
transition: all 0.2s ease;
}
.page-link:hover {
background-color: #f0f0f0;
}
.page-link.active {
background-color: #333;
color: white;
border-color: #333;
}
/* Article detail page */
.article-detail {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.article-detail-title {
font-size: 2.5rem;
color: #333;
margin-bottom: 1rem;
line-height: 1.2;
}
.article-detail-date {
font-size: 1rem;
color: #666;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
}
.article-detail-content {
color: #333;
line-height: 1.6;
}
.article-detail-content h1,
.article-detail-content h2,
.article-detail-content h3,
.article-detail-content h4,
.article-detail-content h5,
.article-detail-content h6 {
margin-top: 2rem;
margin-bottom: 1rem;
color: #333;
}
.article-detail-content p {
margin-bottom: 1.5rem;
}
.article-detail-content ul,
.article-detail-content ol {
margin-bottom: 1.5rem;
padding-left: 2rem;
}
.article-detail-content li {
margin-bottom: 0.5rem;
}
.article-detail-content img {
max-width: 100%;
height: auto;
margin: 2rem 0;
border-radius: 8px;
}
@media (max-width: 900px) { @media (max-width: 900px) {
.data-layout { .data-layout {
flex-direction: column; flex-direction: column;
@ -240,4 +391,16 @@ main {
.data-sidebar { .data-sidebar {
width: 100%; width: 100%;
} }
.pagination {
justify-content: center;
}
.article-detail {
padding: 1.5rem;
}
.article-detail-title {
font-size: 2rem;
}
} }

52
utils/articleUtils.js Normal file
View File

@ -0,0 +1,52 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
export function getArticles() {
const articlesDirectory = path.join(process.cwd(), 'content', 'articles')
const fileNames = fs.readdirSync(articlesDirectory)
const articles = fileNames
.filter(fileName => fileName.endsWith('.md'))
.map(fileName => {
const fullPath = path.join(articlesDirectory, fileName)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
// 从文件名中提取日期(假设文件名格式为 YYYY-MM-DD-article-title.md
const dateMatch = fileName.match(/^(\d{4}-\d{2}-\d{2})-/)
const date = dateMatch ? dateMatch[1] : ''
// 提取简介前150个字符
const excerpt = content.substring(0, 150) + '...'
return {
id: fileName.replace('.md', ''),
date,
title: data.title || fileName.replace(/\d{4}-\d{2}-\d{2}-/, '').replace('.md', ''),
excerpt,
content
}
})
// 按日期排序,最近的优先
return articles.sort((a, b) => {
if (a.date && b.date) {
return new Date(b.date) - new Date(a.date)
}
return 0
})
}
export function getPagedArticles(page = 1, pageSize = 5) {
const allArticles = getArticles()
const startIndex = (page - 1) * pageSize
const endIndex = startIndex + pageSize
return {
articles: allArticles.slice(startIndex, endIndex),
total: allArticles.length,
totalPages: Math.ceil(allArticles.length / pageSize),
currentPage: page
}
}