保存本地对routes.ts的修改
Change-Id: I4f4dbd8069893d7363e251130791dc0594be44e1
diff --git a/src/components/BugReportSection.tsx b/src/components/BugReportSection.tsx
new file mode 100644
index 0000000..a512fc6
--- /dev/null
+++ b/src/components/BugReportSection.tsx
@@ -0,0 +1,159 @@
+import React, { useState, useEffect } from 'react';
+import { List, Button, Form, Input, Select, message, Typography, Tag, Divider, Pagination } from 'antd';
+import { BugOutlined } from '@ant-design/icons';
+import WorkAPI from '../api/workApi';
+import type { BugReport } from '../api/otherType';
+
+const { Text } = Typography;
+const { Option } = Select;
+const { TextArea } = Input;
+
+interface BugReportForm {
+ title: string;
+ description: string;
+ severity: 'low' | 'medium' | 'high';
+}
+
+const BugReportSection: React.FC<{ workId: number }> = ({ workId }) => {
+ const [bugs, setBugs] = useState<BugReport[]>([]);
+ const [loading, setLoading] = useState(false);
+ const [form] = Form.useForm();
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(5); // 每页显示5条
+
+ // 加载Bug反馈列表
+ useEffect(() => {
+ const loadBugs = async () => {
+ try {
+ setLoading(true);
+ const bugList = await WorkAPI.getBugReports(workId);
+ setBugs(bugList);
+ } catch (error) {
+ message.error('加载Bug反馈失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ loadBugs();
+ }, [workId]);
+
+ // 获取当前页的Bug数据
+ const getCurrentPageBugs = () => {
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ return bugs.slice(startIndex, endIndex);
+ };
+
+ // 提交Bug反馈
+ const handleSubmit = async (values: BugReportForm) => {
+ try {
+ await WorkAPI.reportBug(workId, values);
+ message.success('Bug反馈提交成功');
+ form.resetFields();
+ // 重新加载Bug列表
+ const bugList = await WorkAPI.getBugReports(workId);
+ setBugs(bugList);
+ // 重置到第一页
+ setCurrentPage(1);
+ } catch (error) {
+ message.error('Bug反馈提交失败');
+ }
+ };
+
+ const getSeverityColor = (severity: string) => {
+ switch (severity) {
+ case 'high': return 'red';
+ case 'medium': return 'orange';
+ default: return 'green';
+ }
+ };
+
+ return (
+ <div className="bug-report-section">
+ <Divider orientation="left">
+ <BugOutlined /> Bug反馈
+ </Divider>
+
+ <Form form={form} onFinish={handleSubmit} layout="vertical">
+ <Form.Item
+ name="title"
+ label="Bug标题"
+ rules={[{ required: true, message: '请输入Bug标题' }]}
+ >
+ <Input placeholder="简要描述Bug" />
+ </Form.Item>
+
+ <Form.Item
+ name="severity"
+ label="严重程度"
+ initialValue="medium"
+ rules={[{ required: true }]}
+ >
+ <Select>
+ <Option value="low">低</Option>
+ <Option value="medium">中</Option>
+ <Option value="high">高</Option>
+ </Select>
+ </Form.Item>
+
+ <Form.Item
+ name="description"
+ label="详细描述"
+ rules={[{ required: true, message: '请详细描述Bug' }]}
+ >
+ <TextArea rows={4} placeholder="请详细描述Bug出现的步骤、现象和预期结果" />
+ </Form.Item>
+
+ <Form.Item>
+ <Button type="primary" htmlType="submit">
+ 提交Bug
+ </Button>
+ </Form.Item>
+ </Form>
+
+ <Divider orientation="left">Bug列表</Divider>
+
+ <List
+ dataSource={getCurrentPageBugs()}
+ loading={loading}
+ renderItem={bug => (
+ <List.Item>
+ <div className="bug-item">
+ <div className="bug-header">
+ <Text strong>{bug.title}</Text>
+ <Tag color={getSeverityColor(bug.severity)}>
+ {bug.severity === 'high' ? '严重' : bug.severity === 'medium' ? '中等' : '轻微'}
+ </Tag>
+ </div>
+ <Text type="secondary">报告人: {bug.reporter} | 时间: {new Date(bug.createTime).toLocaleString()}</Text>
+ <div className="bug-content">
+ <Text>{bug.description}</Text>
+ </div>
+ <Tag color={bug.status === 'open' ? 'red' : bug.status === 'resolved' ? 'green' : 'gray'}>
+ {bug.status === 'open' ? '未解决' : bug.status === 'resolved' ? '已解决' : '不予解决'}
+ </Tag>
+ </div>
+ </List.Item>
+ )}
+ />
+
+ <div style={{ textAlign: 'right', marginTop: 16 }}>
+ <Pagination
+ current={currentPage}
+ pageSize={pageSize}
+ total={bugs.length}
+ onChange={(page, size) => {
+ setCurrentPage(page);
+ setPageSize(size);
+ }}
+ showSizeChanger
+ showQuickJumper
+ showTotal={total => `共 ${total} 条`}
+ />
+ </div>
+ </div>
+ );
+};
+
+export default BugReportSection;
\ No newline at end of file
diff --git a/src/components/CustomComment.css b/src/components/CustomComment.css
new file mode 100644
index 0000000..8f9a012
--- /dev/null
+++ b/src/components/CustomComment.css
@@ -0,0 +1,54 @@
+/* src/components/CustomComment.css */
+.custom-comment {
+ margin-bottom: 16px;
+ padding: 16px 0;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.custom-comment:last-child {
+ border-bottom: none;
+}
+
+.custom-comment-inner {
+ display: flex;
+}
+
+.custom-comment-avatar {
+ margin-right: 12px;
+}
+
+.custom-comment-content {
+ flex: 1;
+}
+
+.custom-comment-header {
+ margin-bottom: 8px;
+ display: flex;
+ align-items: center;
+}
+
+.custom-comment-author {
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.85);
+}
+
+.custom-comment-datetime {
+ margin-left: 12px;
+ font-size: 12px;
+ color: rgba(0, 0, 0, 0.45);
+}
+
+.custom-comment-text {
+ margin-bottom: 8px;
+ color: rgba(0, 0, 0, 0.85);
+ white-space: pre-line;
+}
+
+.custom-comment-actions {
+ margin-top: 8px;
+}
+
+.custom-comment-action {
+ padding: 0 4px;
+ font-size: 12px;
+}
\ No newline at end of file
diff --git a/src/components/DiscussionSection.tsx b/src/components/DiscussionSection.tsx
new file mode 100644
index 0000000..7fd39c4
--- /dev/null
+++ b/src/components/DiscussionSection.tsx
@@ -0,0 +1,190 @@
+import React, { useState, useEffect } from 'react';
+import { List, Button, Form, Input, message, Avatar, Typography, Modal, Pagination } from 'antd';
+import { LikeOutlined, MessageOutlined } from '@ant-design/icons';
+import CommentAPI from '../api/commentApi';
+import type { Comment } from '../api/otherType';
+
+const { Text } = Typography;
+const { TextArea } = Input;
+
+interface DiscussionSectionProps {
+ workId: number;
+}
+
+const DiscussionSection: React.FC<DiscussionSectionProps> = ({ workId }) => {
+ const [comments, setComments] = useState<Comment[]>([]);
+ const [loading, setLoading] = useState(false);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [form] = Form.useForm();
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(5); // 每页显示5条
+
+ // 加载评论
+ useEffect(() => {
+ const loadComments = async () => {
+ try {
+ setLoading(true);
+ const commentList = await CommentAPI.getCommentsByPostId(workId);
+ setComments(commentList);
+ } catch (error) {
+ message.error('加载评论失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ loadComments();
+ }, [workId]);
+
+ // 获取当前页的评论数据
+ const getCurrentPageComments = () => {
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ return comments.slice(startIndex, endIndex);
+ };
+
+ // 提交新评论
+ const handleSubmit = async (values: { title: string; content: string }) => {
+ try {
+ await CommentAPI.addComment({
+ postId: workId,
+ content: values.content
+ });
+ message.success('评论添加成功');
+ form.resetFields();
+ setModalVisible(false);
+ // 重新加载评论
+ const commentList = await CommentAPI.getCommentsByPostId(workId);
+ setComments(commentList);
+ // 重置到第一页
+ setCurrentPage(1);
+ } catch (error) {
+ message.error('评论添加失败');
+ }
+ };
+
+ // 点赞评论
+ const handleLikeComment = async (commentId: number) => {
+ try {
+ await CommentAPI.likeComment(commentId);
+ setComments(prev => prev.map(comment =>
+ comment.id === commentId
+ ? { ...comment, likes: (comment.likes || 0) + 1 }
+ : comment
+ ));
+ } catch (error) {
+ message.error('点赞失败');
+ }
+ };
+
+ return (
+ <div className="discussion-section">
+ <Button
+ type="primary"
+ icon={<MessageOutlined />}
+ onClick={() => setModalVisible(true)}
+ style={{ marginBottom: 16 }}
+ >
+ 新建讨论
+ </Button>
+
+ <List
+ dataSource={getCurrentPageComments()}
+ loading={loading}
+ renderItem={comment => (
+ <List.Item className="comment-item">
+ <div className="comment-content">
+ <div className="comment-header">
+ <Avatar size="small">
+ {comment.author?.charAt(0) || '?'}
+ </Avatar>
+ <Text strong>{comment.author || '匿名用户'}</Text>
+ <Text type="secondary">
+ {comment.createTime ? new Date(comment.createTime).toLocaleString() : '未知时间'}
+ </Text>
+ </div>
+ <div className="comment-body">
+ <Text>{comment.content}</Text>
+ </div>
+ <div className="comment-actions">
+ <Button
+ type="text"
+ icon={<LikeOutlined />}
+ onClick={() => handleLikeComment(comment.id)}
+ >
+ {comment.likes || 0}
+ </Button>
+ </div>
+ </div>
+ </List.Item>
+ )}
+ />
+
+ <div style={{ textAlign: 'right', marginTop: 16 }}>
+ <Pagination
+ current={currentPage}
+ pageSize={pageSize}
+ total={comments.length}
+ onChange={(page, size) => {
+ setCurrentPage(page);
+ setPageSize(size);
+ }}
+ showSizeChanger
+ showQuickJumper
+ showTotal={total => `共 ${total} 条`}
+ />
+ </div>
+
+ <Modal
+ title={
+ <span>
+ <MessageOutlined style={{ marginRight: 8 }} />
+ 新建讨论
+ </span>
+ }
+ visible={modalVisible}
+ onCancel={() => setModalVisible(false)}
+ footer={[
+ <Button key="cancel" onClick={() => setModalVisible(false)}>
+ 取消
+ </Button>,
+ <Button
+ key="submit"
+ type="primary"
+ onClick={() => {
+ form.validateFields()
+ .then(values => handleSubmit(values))
+ .catch(() => message.error('请填写完整信息'));
+ }}
+ >
+ 创建讨论
+ </Button>,
+ ]}
+ >
+ <Form form={form} layout="vertical">
+ <Form.Item
+ name="title"
+ label="讨论主题"
+ rules={[{ required: true, message: '请输入讨论主题' }]}
+ >
+ <Input placeholder="输入讨论主题" />
+ </Form.Item>
+
+ <Form.Item
+ name="content"
+ label="讨论内容"
+ rules={[{ required: true, message: '请输入讨论内容' }]}
+ >
+ <TextArea
+ rows={6}
+ placeholder="详细描述你想讨论的内容..."
+ />
+ </Form.Item>
+ </Form>
+ </Modal>
+ </div>
+ );
+};
+
+
+export default DiscussionSection;
\ No newline at end of file