diff --git a/miniprogram/src/app.config.js b/miniprogram/src/app.config.js
index 9af9480..c477e5a 100644
--- a/miniprogram/src/app.config.js
+++ b/miniprogram/src/app.config.js
@@ -12,6 +12,8 @@ export default {
'pages/park-apply/index',
'pages/cert-apply/index',
'pages/park-transfer/index',
+ 'pages/content/index',
+ 'pages/content-detail/index',
'pages/scan-login/index'
],
window: {
@@ -31,8 +33,7 @@ export default {
{ pagePath: 'pages/booking/index', text: '活动' },
{ pagePath: 'pages/test/index', text: '测评' },
{ pagePath: 'pages/index/index', text: '首页' },
- { pagePath: 'pages/policy/index', text: '政策' },
- // { pagePath: 'pages/plan/index', text: '流程' },
+ { pagePath: 'pages/content/index', text: '资讯' },
{ pagePath: 'pages/mine/index', text: '我的' }
]
}
diff --git a/miniprogram/src/custom-tab-bar/index.jsx b/miniprogram/src/custom-tab-bar/index.jsx
index 81bd9ac..9109ef0 100644
--- a/miniprogram/src/custom-tab-bar/index.jsx
+++ b/miniprogram/src/custom-tab-bar/index.jsx
@@ -8,7 +8,7 @@ const LIST = [
{ pagePath: '/pages/booking/index', text: '活动', icon: 'book' },
{ pagePath: '/pages/test/index', text: '测评', icon: 'compass' },
{ pagePath: '/pages/index/index', text: 'OPC', icon: 'star', center: true },
- { pagePath: '/pages/policy/index', text: '政策', icon: 'shield' },
+ { pagePath: '/pages/content/index', text: '资讯', icon: 'shield' },
{ pagePath: '/pages/mine/index', text: '我的', icon: 'user' }
];
diff --git a/miniprogram/src/pages/content-detail/index.config.js b/miniprogram/src/pages/content-detail/index.config.js
new file mode 100644
index 0000000..137fffe
--- /dev/null
+++ b/miniprogram/src/pages/content-detail/index.config.js
@@ -0,0 +1 @@
+export default { navigationBarTitleText: '资讯详情' };
diff --git a/miniprogram/src/pages/content-detail/index.jsx b/miniprogram/src/pages/content-detail/index.jsx
new file mode 100644
index 0000000..dd71f4e
--- /dev/null
+++ b/miniprogram/src/pages/content-detail/index.jsx
@@ -0,0 +1,35 @@
+import { View, Text } from '@tarojs/components';
+import Taro from '@tarojs/taro';
+import { useState, useEffect } from 'react';
+import { getContent } from '@/utils/content';
+import './index.scss';
+
+/** 资讯详情:展示单条 title/summary/body。 */
+export default function ContentDetail() {
+ const [item, setItem] = useState(null);
+ const [err, setErr] = useState('');
+ const [loaded, setLoaded] = useState(false);
+
+ useEffect(() => {
+ const inst = Taro.getCurrentInstance();
+ const id = (inst && inst.router && inst.router.params && inst.router.params.id) || '';
+ if (!id) { setErr('无效内容'); setLoaded(true); return; }
+ getContent(id).then(setItem).catch(() => setErr('加载失败')).finally(() => setLoaded(true));
+ }, []);
+
+ if (!loaded) return 加载中…;
+ if (err || !item) return {err || '内容不存在'};
+
+ return (
+
+ {item.title}
+ 发布于 {fmt(item.created_at)}
+ {item.summary ? {item.summary} : null}
+ {item.body || '暂无正文'}
+
+ );
+}
+
+function fmt(s) {
+ return (s || '').replace('T', ' ').replace('Z', '').slice(0, 16);
+}
diff --git a/miniprogram/src/pages/content-detail/index.scss b/miniprogram/src/pages/content-detail/index.scss
new file mode 100644
index 0000000..8ede336
--- /dev/null
+++ b/miniprogram/src/pages/content-detail/index.scss
@@ -0,0 +1,7 @@
+/* 资讯详情 · 小程序样式(暗色令牌一致) */
+.cd { position: relative; z-index: 2; padding: 32rpx 28rpx 60rpx; }
+.cd-title { display: block; font-size: 36rpx; color: #fff; font-weight: 700; line-height: 1.4; }
+.cd-meta { display: block; margin-top: 14rpx; font-size: 23rpx; color: #7c7c80; }
+.cd-summary { display: block; margin-top: 20rpx; font-size: 27rpx; color: #c4c2c3; line-height: 1.7; }
+.cd-body { display: block; margin-top: 24rpx; font-size: 28rpx; color: #e4e4e6; line-height: 1.8; white-space: pre-wrap; }
+.cd-err { display: block; padding: 90rpx 0; text-align: center; font-size: 26rpx; color: #8e8e8e; }
diff --git a/miniprogram/src/pages/content/index.config.js b/miniprogram/src/pages/content/index.config.js
new file mode 100644
index 0000000..6b011a7
--- /dev/null
+++ b/miniprogram/src/pages/content/index.config.js
@@ -0,0 +1 @@
+export default { navigationBarTitleText: '资讯中心' };
diff --git a/miniprogram/src/pages/content/index.jsx b/miniprogram/src/pages/content/index.jsx
new file mode 100644
index 0000000..ebc0e84
--- /dev/null
+++ b/miniprogram/src/pages/content/index.jsx
@@ -0,0 +1,65 @@
+import { View, Text } from '@tarojs/components';
+import Taro from '@tarojs/taro';
+import { useState, useEffect } from 'react';
+import { listContent, CONTENT_CATS } from '@/utils/content';
+import { useTabIndex } from '@/utils/tab';
+import Skeleton from '@/components/skeleton';
+import './index.scss';
+
+/** 资讯中心(C 端 tab):按类别(政策/资讯/技能/动态)展示 admin 发布的资讯。 */
+export default function ContentIndex() {
+ useTabIndex(3);
+ const [cat, setCat] = useState('policy');
+ const [items, setItems] = useState([]);
+ const [err, setErr] = useState('');
+ const [loaded, setLoaded] = useState(false);
+
+ useEffect(() => {
+ setLoaded(false); setErr('');
+ listContent(cat)
+ .then(setItems)
+ .catch(() => setErr('加载失败,请稍后重试'))
+ .finally(() => setLoaded(true));
+ }, [cat]);
+
+ const go = (id) => Taro.navigateTo({ url: `/pages/content-detail/index?id=${id}` });
+ const goQuiz = () => Taro.navigateTo({ url: '/pages/policy/index' });
+
+ return (
+
+ {/* 类别分段 */}
+
+ {CONTENT_CATS.map((c) => (
+ setCat(c.key)}>{c.label}
+ ))}
+
+
+ {/* 政策测评入口(非 tab,政策分类下提供) */}
+ {cat === 'policy' && (
+
+ 政策测评
+ 测一测 →
+
+ )}
+
+ {err ? {err} : null}
+ {loaded ? (
+ items.length === 0
+ ? 暂无{CONTENT_CATS.find((c) => c.key === cat)?.label || ''}内容
+ : items.map((it) => (
+ go(it.id)}>
+ {it.title}
+ {it.summary ? {it.summary} : null}
+ {fmt(it.created_at)}
+
+ ))
+ ) : (
+
+ )}
+
+ );
+}
+
+function fmt(s) {
+ return (s || '').replace('T', ' ').replace('Z', '').slice(0, 16);
+}
diff --git a/miniprogram/src/pages/content/index.scss b/miniprogram/src/pages/content/index.scss
new file mode 100644
index 0000000..4e56820
--- /dev/null
+++ b/miniprogram/src/pages/content/index.scss
@@ -0,0 +1,25 @@
+/* 资讯中心 · 小程序样式(暗色令牌一致) */
+.ct { position: relative; z-index: 2; padding: 16rpx 24rpx 60rpx; }
+
+.ct-cats { display: flex; gap: 10rpx; padding: 8rpx 0 16rpx; }
+.ct-cat {
+ flex: 1; text-align: center; padding: 14rpx 0; border-radius: 12rpx;
+ background: #17171b; border: 1rpx solid #2a2a2d; color: #9b9b9b; font-size: 27rpx;
+}
+.ct-cat.on { background: #fff; color: #111; border-color: #fff; font-weight: 600; }
+
+.ct-quiz {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 18rpx 22rpx; margin-bottom: 8rpx; border-radius: 14rpx;
+ background: linear-gradient(180deg, #14141a, #0e0e12); border: 1rpx solid rgba(255,255,255,.12);
+}
+.ct-quiz-l { font-size: 27rpx; color: #fff; font-weight: 600; }
+.ct-quiz-d { font-size: 24rpx; color: #ffd28a; }
+
+.ct-item { padding: 22rpx 4rpx; border-bottom: 1rpx solid rgba(255,255,255,.08); }
+.ct-item-title { display: block; font-size: 29rpx; color: #fff; font-weight: 600; line-height: 1.5; }
+.ct-item-summary { display: block; margin-top: 8rpx; font-size: 25rpx; color: #a9a9ac; line-height: 1.6; }
+.ct-item-time { display: block; margin-top: 10rpx; font-size: 22rpx; color: #7c7c80; }
+
+.ct-empty { padding: 80rpx 0; text-align: center; font-size: 26rpx; color: #8e8e8e; }
+.ct-err { display: block; padding: 60rpx 0; text-align: center; font-size: 26rpx; color: #f0b8b8; }
diff --git a/miniprogram/src/utils/content.js b/miniprogram/src/utils/content.js
new file mode 100644
index 0000000..67b508c
--- /dev/null
+++ b/miniprogram/src/utils/content.js
@@ -0,0 +1,26 @@
+import { request, cachedRequest } from './api';
+
+/** 资讯中心类别 */
+export const CONTENT_CATS = [
+ { key: 'policy', label: '政策' },
+ { key: 'news', label: '资讯' },
+ { key: 'skill', label: '技能' },
+ { key: 'dynamic', label: '动态' },
+];
+
+/** 获取某类别已发布资讯(公开浏览) */
+export async function listContent(type) {
+ const r = await request('GET', `/opc/content?type=${type}`, {}, false);
+ return Array.isArray(r?.items) ? r.items : [];
+}
+
+/** 带短缓存的资讯列表(切换分类时避免跳变) */
+export function cachedContent(type) {
+ return cachedRequest('GET', `/opc/content?type=${type}`, 30000, {}, false);
+}
+
+/** 资讯详情 */
+export async function getContent(id) {
+ const r = await request('GET', `/opc/content/${id}`, {}, false);
+ return r || null;
+}