From fd84b0b0ff9e5fda3b65902e21ff18320cf5ffc5 Mon Sep 17 00:00:00 2001 From: zty Date: Tue, 7 Jul 2026 03:34:48 -0400 Subject: [PATCH] feat(web): forum home page with boards, list, post --- .../源码/用户端/iapip-web/.umirc.ts | 126 ++++++++++++++++++ .../iapip-web/src/pages/forum/index.tsx | 125 +++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 空气质量预测/源码/用户端/iapip-web/.umirc.ts create mode 100644 空气质量预测/源码/用户端/iapip-web/src/pages/forum/index.tsx diff --git a/空气质量预测/源码/用户端/iapip-web/.umirc.ts b/空气质量预测/源码/用户端/iapip-web/.umirc.ts new file mode 100644 index 0000000..dab16d1 --- /dev/null +++ b/空气质量预测/源码/用户端/iapip-web/.umirc.ts @@ -0,0 +1,126 @@ +import { defineConfig } from '@umijs/max' + +const COLOR_PRIMARY = '#B43533' +const COLOR_TEXT_PRIMARY = 'rgba(0, 0, 0, 0.88)' +const COLOR_TEXT_DISABLED = 'rgba(0, 0, 0, 0.25)' + +export default defineConfig({ + initialState: {}, + access: {}, + model: {}, + antd: { + theme: { + token: { + colorPrimary: COLOR_PRIMARY, + colorLink: COLOR_PRIMARY, + colorTextDisabled: COLOR_TEXT_PRIMARY + }, + components: { + Button: { + defaultBorderColor: COLOR_PRIMARY, + defaultColor: COLOR_PRIMARY, + colorTextDisabled: COLOR_TEXT_DISABLED + }, + Cascader: { + dropdownHeight: 460 + } + } + } + }, + title: '装修室内空气质量预测系统', + layout: { + locale: false + }, + tailwindcss: {}, + routes: [ + { + name: '登录', + path: '/login', + component: './login', + layout: false + }, + { + name: '落地页', + path: '/landing', + component: './landing', + layout: false + }, + { + name: '健康装修大家谈', + path: '/forum', + component: './forum', + layout: false + }, + { + name: '帖子详情', + path: '/forum/thread/:id', + component: './forum/thread', + layout: false + }, + { + path: '/', + redirect: '/landing' + }, + { + name: '首页', + path: '/home', + component: './home' + }, + { + name: '污染源识别', + path: '/source', + component: './source' + }, + { + name: '模板库', + path: '/template', + component: './template' + }, + { + name: '材料库', + path: '/material', + component: './material' + }, + { + name: '材料类别管理', + path: '/material-category', + component: './material-category', + access: 'canMtrlCatMgmt' + }, + { + name: '历史记录', + path: '/history-project', + component: './history-project' + }, + { + name: '个人设置', + path: '/personal-settings', + component: './personal-settings', + hideInMenu: true + } + ], + mock: false, + proxy: { + '/api': { + target: 'http://localhost:6060', + changeOrigin: true + }, + '/socket.io': { + target: 'http://localhost:6060', + ws: true + } + }, + history: { + type: 'hash' + }, + publicPath: '/iapip-web/', + favicons: ['/iapip-web/favicon.ico'], + request: { + dataField: '' + }, + mfsu: { + strategy: 'normal' + }, + esbuildMinifyIIFE: true, + npmClient: 'pnpm' +}) diff --git a/空气质量预测/源码/用户端/iapip-web/src/pages/forum/index.tsx b/空气质量预测/源码/用户端/iapip-web/src/pages/forum/index.tsx new file mode 100644 index 0000000..cf8fd9d --- /dev/null +++ b/空气质量预测/源码/用户端/iapip-web/src/pages/forum/index.tsx @@ -0,0 +1,125 @@ +import { useEffect, useState, useCallback } from 'react' +import { history } from '@umijs/max' +import { Tabs, List, Button, Modal, Form, Input, Select, Pagination, message, Empty } from 'antd' +import api from '@/services/api' +import type { ForumThreadListItem } from '@/services/api/forum' +import { LOC_STOR_KEY } from '@/common/constants' +import PhoneAuthModal from '@/components/phone-auth-modal' + +const hasToken = () => !!localStorage.getItem(LOC_STOR_KEY.AUTH_TOKEN) +const PAGE_SIZE = 10 + +export default function ForumHome() { + const [boards, setBoards] = useState<{ board: string, count: number }[]>([]) + const [active, setActive] = useState('all') + const [list, setList] = useState([]) + const [total, setTotal] = useState(0) + const [page, setPage] = useState(1) + const [loading, setLoading] = useState(false) + + const [authOpen, setAuthOpen] = useState(false) + const [postOpen, setPostOpen] = useState(false) + const [form] = Form.useForm() + + const loadBoards = useCallback(async () => { + try { setBoards(await api.forum.getBoards()) } catch { /* 全局提示 */ } + }, []) + + const loadThreads = useCallback(async (p: number, board: string) => { + setLoading(true) + try { + const res = await api.forum.getThreads({ + board: board === 'all' ? undefined : board, + page: p, + size: PAGE_SIZE + }) + setList(res.list) + setTotal(res.total) + } catch { /* 全局提示 */ } finally { setLoading(false) } + }, []) + + useEffect(() => { loadBoards() }, [loadBoards]) + useEffect(() => { loadThreads(page, active) }, [page, active, loadThreads]) + + const onTab = (key: string) => { setActive(key); setPage(1) } + + const openPost = () => { + if (!hasToken()) { setAuthOpen(true); return } + form.resetFields() + setPostOpen(true) + } + + const submitPost = async () => { + const v = await form.validateFields() + try { + await api.forum.createThread(v) + message.success('发布成功') + setPostOpen(false) + setActive(v.board); setPage(1) + loadThreads(1, v.board) + loadBoards() + } catch { /* 全局提示 */ } + } + + const boardOptions = boards.map(b => ({ label: `${b.board} (${b.count})`, value: b.board })) + + return ( +
+
+ history.push('/landing')} style={{ color: '#888' }}>← 返回首页 +

健康装修大家谈

+ +
+ + ({ key: b.board, label: `${b.board} (${b.count})` }))]} + /> + + }} + dataSource={list} + renderItem={t => ( + history.push(`/forum/thread/${t.id}`)} + actions={[💬 {t.reply_count}, 👍 {t.like_count}]} + > + [{t.board}] {t.title}} + description={`${t.author_name} · ${new Date(t.created_at).toLocaleString()}`} + /> + + )} + /> + + {total > PAGE_SIZE && ( +
+ +
+ )} + + setPostOpen(false)} okText="发布" destroyOnClose> +
+ + + + + + +
+
+ + { setAuthOpen(false); openPost() }} + onCancel={() => setAuthOpen(false)} + /> +
+ ) +}