feat: 论坛新增 & 列表页面修改
This commit is contained in:
parent
3a1c04686a
commit
664e2950f7
@ -1,62 +1,83 @@
|
|||||||
.forumContainer {
|
.forumContainer {
|
||||||
|
background: #f6f8fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 32px 0;
|
||||||
|
}
|
||||||
.searchBar {
|
.searchBar {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 16px;
|
border-radius: 12px;
|
||||||
border-radius: 8px;
|
margin: 0 auto 24px auto;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
// max-width: 1100px;
|
||||||
margin-bottom: 16px;
|
box-shadow: 0 2px 8px #f0f1f2;
|
||||||
|
padding: 24px 32px;
|
||||||
|
}
|
||||||
|
.listCard {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
// max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 2px 16px #f0f1f2;
|
||||||
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postItem {
|
.postItem {
|
||||||
transition: all 0.3s;
|
border-radius: 10px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
background: #fafbfc;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #fafafa;
|
box-shadow: 0 4px 16px #e6e6e6;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.postTitle {
|
.postTitle {
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
color: #1a1a1a;
|
color: #222;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #1890ff;
|
color: #1677ff;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.categoryTag {
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.topBadge {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
.postMeta {
|
.postMeta {
|
||||||
color: #8c8c8c;
|
color: #888;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.author {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.postContent {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #444;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.7;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.coverImage {
|
||||||
|
border-radius: 8px;
|
||||||
|
object-fit: cover;
|
||||||
|
box-shadow: 0 2px 8px #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postStats {
|
.postStats {
|
||||||
color: #595959;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
color: #888;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
.statItem {
|
.statItem {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coverImage {
|
|
||||||
border-radius: 8px;
|
|
||||||
object-fit: cover;
|
|
||||||
transition: transform 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.categoryTag {
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 2px 12px;
|
|
||||||
}
|
}
|
||||||
|
.publishBtn {
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Card, List, Tag, Space, Input, Button, Select, Badge } from 'antd';
|
import { Card, List, Tag, Space, Input, Button, Select, Badge, Avatar } from 'antd';
|
||||||
import { history } from '@umijs/max';
|
import { history } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
@ -8,80 +8,34 @@ import {
|
|||||||
LikeOutlined,
|
LikeOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
import { listAllPostsUsingGet } from '@/services/hebi/postController';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
|
||||||
const ForumList: React.FC = () => {
|
const ForumList: React.FC = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [filter, setFilter] = useState('all');
|
const [filter, setFilter] = useState('all');
|
||||||
|
const [postList, setPostList] = useState<any[]>([]);
|
||||||
|
|
||||||
const mockPosts = [
|
// 拉取帖子数据
|
||||||
{
|
useEffect(() => {
|
||||||
id: 1,
|
const fetchPosts = async () => {
|
||||||
title: '使用 GPT-4 进行高级数据分析的实践经验',
|
setLoading(true);
|
||||||
category: '技术讨论',
|
try {
|
||||||
author: '数据专家',
|
const res = await listAllPostsUsingGet();
|
||||||
createTime: '2024-01-15 14:30',
|
if (res && res.code === 0 && Array.isArray(res.data)) {
|
||||||
description: '分享在大规模数据集上使用 GPT-4 进行智能分析的经验,包括提示词工程、数据预处理技巧,以及如何提高分析准确率...',
|
setPostList(res.data);
|
||||||
views: 2150,
|
} else {
|
||||||
comments: 156,
|
setPostList([]);
|
||||||
likes: 342,
|
}
|
||||||
isTop: true,
|
} catch (e) {
|
||||||
cover: 'https://picsum.photos/272/153',
|
setPostList([]);
|
||||||
},
|
}
|
||||||
{
|
setLoading(false);
|
||||||
id: 2,
|
};
|
||||||
title: 'AI 辅助数据可视化最佳实践',
|
fetchPosts();
|
||||||
category: '经验分享',
|
}, []);
|
||||||
author: '可视化工程师',
|
|
||||||
createTime: '2024-01-14 16:20',
|
|
||||||
description: '探讨如何利用 AI 技术自动生成数据可视化方案,包括图表类型选择、配色方案优化、以及交互设计的智能推荐...',
|
|
||||||
views: 1856,
|
|
||||||
comments: 89,
|
|
||||||
likes: 267,
|
|
||||||
isTop: false,
|
|
||||||
cover: 'https://picsum.photos/272/153?random=2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: '智能预测模型准确率问题求助',
|
|
||||||
category: '问题求助',
|
|
||||||
author: '数据新手',
|
|
||||||
createTime: '2024-01-13 09:45',
|
|
||||||
description: '在使用系统进行销售预测时发现准确率不够理想,数据预处理已经做了基础清洗,请问还有哪些方面需要优化?...',
|
|
||||||
views: 632,
|
|
||||||
comments: 42,
|
|
||||||
likes: 28,
|
|
||||||
isTop: false,
|
|
||||||
cover: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: 'AIGC 在金融数据分析中的应用实践',
|
|
||||||
category: '技术讨论',
|
|
||||||
author: '金融分析师',
|
|
||||||
createTime: '2024-01-12 11:30',
|
|
||||||
description: '分享我们团队使用 AIGC 技术进行金融市场分析的经验,包括风险评估、趋势预测和投资建议生成的完整流程...',
|
|
||||||
views: 1967,
|
|
||||||
comments: 156,
|
|
||||||
likes: 420,
|
|
||||||
isTop: true,
|
|
||||||
cover: 'https://picsum.photos/272/153?random=4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: '大规模数据集的智能分析方法论',
|
|
||||||
category: '经验分享',
|
|
||||||
author: '资深数据科学家',
|
|
||||||
createTime: '2024-01-11 15:15',
|
|
||||||
description: '详细介绍如何处理和分析大规模数据集,包括数据清洗策略、特征工程技巧、模型选择以及结果验证方法...',
|
|
||||||
views: 2543,
|
|
||||||
comments: 189,
|
|
||||||
likes: 534,
|
|
||||||
isTop: false,
|
|
||||||
cover: 'https://picsum.photos/272/153?random=5',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{ value: 'all', label: '全部' },
|
{ value: 'all', label: '全部' },
|
||||||
@ -91,9 +45,15 @@ const ForumList: React.FC = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleSearch = (value: string) => {
|
const handleSearch = (value: string) => {
|
||||||
|
// 可根据 value 进行前端过滤或请求接口
|
||||||
console.log('搜索:', value);
|
console.log('搜索:', value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 分类过滤
|
||||||
|
const filteredPosts = filter === 'all'
|
||||||
|
? postList
|
||||||
|
: postList.filter(item => item.tagList && item.tagList.includes(filter));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.forumContainer}>
|
<div className={styles.forumContainer}>
|
||||||
<Card className={styles.searchBar} bordered={false}>
|
<Card className={styles.searchBar} bordered={false}>
|
||||||
@ -132,7 +92,7 @@ const ForumList: React.FC = () => {
|
|||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showTotal: (total) => `共 ${total} 条帖子`,
|
showTotal: (total) => `共 ${total} 条帖子`,
|
||||||
}}
|
}}
|
||||||
dataSource={mockPosts} // 使用模拟数据
|
dataSource={filteredPosts}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={item.id}
|
key={item.id}
|
||||||
@ -140,13 +100,13 @@ const ForumList: React.FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
<Space key={"actions"} className={styles.postStats}>
|
<Space key={"actions"} className={styles.postStats}>
|
||||||
<span key="views" className={styles.statItem}>
|
<span key="views" className={styles.statItem}>
|
||||||
<EyeOutlined /> {item.views}
|
<EyeOutlined /> {item.viewNum || 0}
|
||||||
</span>
|
</span>
|
||||||
<span key="comments" className={styles.statItem}>
|
<span key="comments" className={styles.statItem}>
|
||||||
<MessageOutlined /> {item.comments}
|
<MessageOutlined /> {item.commentNum || 0}
|
||||||
</span>
|
</span>
|
||||||
<span key="likes" className={styles.statItem}>
|
<span key="likes" className={styles.statItem}>
|
||||||
<LikeOutlined /> {item.likes}
|
<LikeOutlined /> {item.favourNum || 0}
|
||||||
</span>
|
</span>
|
||||||
</Space>,
|
</Space>,
|
||||||
]}
|
]}
|
||||||
@ -163,6 +123,13 @@ const ForumList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<List.Item.Meta
|
<List.Item.Meta
|
||||||
|
avatar={
|
||||||
|
item.user?.userAvatar ? (
|
||||||
|
<Avatar src={item.user.userAvatar} />
|
||||||
|
) : (
|
||||||
|
<Avatar>{item.user?.userName?.[0] || '匿'}</Avatar>
|
||||||
|
)
|
||||||
|
}
|
||||||
title={
|
title={
|
||||||
<Space size="middle" align="center">
|
<Space size="middle" align="center">
|
||||||
<a
|
<a
|
||||||
@ -171,9 +138,12 @@ const ForumList: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</a>
|
</a>
|
||||||
<Tag className={styles.categoryTag} color="blue">
|
{/* 展示所有标签 */}
|
||||||
{item.category}
|
{item.tagList && item.tagList.map((tag: string) => (
|
||||||
|
<Tag className={styles.categoryTag} color="blue" key={tag}>
|
||||||
|
{tag}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
))}
|
||||||
{item.isTop && (
|
{item.isTop && (
|
||||||
<Badge color="red" text="置顶" />
|
<Badge color="red" text="置顶" />
|
||||||
)}
|
)}
|
||||||
@ -181,13 +151,15 @@ const ForumList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
description={
|
description={
|
||||||
<Space className={styles.postMeta} size="middle">
|
<Space className={styles.postMeta} size="middle">
|
||||||
<span>{item.author}</span>
|
<span>{item.user?.userName || '匿名用户'}</span>
|
||||||
<span>发布于 {item.createTime}</span>
|
<span>
|
||||||
|
发布于 {dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss')}
|
||||||
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className={styles.postContent}>
|
<div className={styles.postContent}>
|
||||||
{item.description}
|
{item.content}
|
||||||
</div>
|
</div>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
|
@ -1,44 +1,25 @@
|
|||||||
.publishContainer {
|
.publishContainer {
|
||||||
max-width: 800px;
|
width: 80vw;
|
||||||
margin: 0 auto;
|
min-height: 100vh;
|
||||||
padding: 24px;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.publishCard {
|
.publishCard {
|
||||||
border-radius: 8px;
|
width: 80vw;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
:global {
|
padding: 32px 48px 24px 48px;
|
||||||
.ant-card-head {
|
box-shadow: none;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border: none;
|
||||||
padding: 16px 24px;
|
background: #fff;
|
||||||
|
|
||||||
.ant-card-head-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.ant-card-body {
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.formLabel {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uploadHint {
|
|
||||||
color: #666;
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonGroup {
|
.buttonGroup {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
gap: 16px;
|
||||||
gap: 12px;
|
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
.formLabel {
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
@ -1,30 +1,34 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Card, Form, Input, Button, Upload, Select, message, Space, Alert } from 'antd';
|
import { Card, Form, Input, Button, Select, message, Alert } from 'antd';
|
||||||
import { PlusOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
|
||||||
import type { UploadFile } from 'antd/es/upload/interface';
|
|
||||||
import { history } from '@umijs/max';
|
import { history } from '@umijs/max';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
import MDEditor from '@uiw/react-md-editor'; // 直接引入,不用 next/dynamic
|
||||||
|
import '@uiw/react-md-editor/markdown-editor.css';
|
||||||
|
import '@uiw/react-markdown-preview/markdown.css';
|
||||||
|
import { addPostUsingPost } from '@/services/hebi/postController';
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const ForumPublish: React.FC = () => {
|
const ForumPublish: React.FC = () => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [mdContent, setMdContent] = useState<string>(''); // 新增
|
||||||
const categories = [
|
|
||||||
{ value: 'tech', label: '技术讨论', description: '分享技术经验和最佳实践' },
|
|
||||||
{ value: 'share', label: '经验分享', description: '分享数据分析案例和心得' },
|
|
||||||
{ value: 'question', label: '问题求助', description: '寻求技术支持和解决方案' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleSubmit = async (values: any) => {
|
const handleSubmit = async (values: any) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
// 处理表单提交
|
const postAddRequest = {
|
||||||
console.log('提交数据:', values);
|
title: values.title,
|
||||||
|
content: mdContent, // 使用 Markdown 内容
|
||||||
|
tags: values.tags || [],
|
||||||
|
};
|
||||||
|
const res = await addPostUsingPost(postAddRequest);
|
||||||
|
if (res && res.code === 0) {
|
||||||
message.success('发布成功');
|
message.success('发布成功');
|
||||||
history.push('/forum/list');
|
history.push('/forum/list');
|
||||||
|
} else {
|
||||||
|
message.error(res?.message || '发布失败');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('发布失败');
|
message.error('发布失败');
|
||||||
}
|
}
|
||||||
@ -41,7 +45,6 @@ const ForumPublish: React.FC = () => {
|
|||||||
showIcon
|
showIcon
|
||||||
style={{ marginBottom: 24 }}
|
style={{ marginBottom: 24 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
@ -62,72 +65,35 @@ const ForumPublish: React.FC = () => {
|
|||||||
maxLength={100}
|
maxLength={100}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="category"
|
name="tags"
|
||||||
label={<span className={styles.formLabel}>分类</span>}
|
label={<span className={styles.formLabel}>标签</span>}
|
||||||
rules={[{ required: true, message: '请选择分类' }]}
|
|
||||||
tooltip={{
|
|
||||||
title: '选择合适的分类有助于其他用户更好地找到您的帖子',
|
|
||||||
icon: <InfoCircleOutlined />
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Select placeholder="请选择帖子分类">
|
|
||||||
{categories.map(cat => (
|
|
||||||
<Select.Option key={cat.value} value={cat.value}>
|
|
||||||
<Space>
|
|
||||||
{cat.label}
|
|
||||||
<span style={{ color: '#999', fontSize: '12px' }}>
|
|
||||||
({cat.description})
|
|
||||||
</span>
|
|
||||||
</Space>
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name="content"
|
|
||||||
label={<span className={styles.formLabel}>内容</span>}
|
|
||||||
rules={[
|
rules={[
|
||||||
{ required: true, message: '请输入内容' },
|
{ required: false, type: 'array', message: '请输入标签' }
|
||||||
{ min: 20, message: '内容至少20个字符' }
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<TextArea
|
<Select
|
||||||
rows={12}
|
mode="tags"
|
||||||
placeholder="请详细描述您要分享的内容..."
|
style={{ width: '100%' }}
|
||||||
showCount
|
placeholder="请输入标签,回车分隔"
|
||||||
maxLength={5000}
|
tokenSeparators={[',', ',']}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{/* Markdown 编辑器替换内容输入 */}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={<span className={styles.formLabel}>封面图</span>}
|
label={<span className={styles.formLabel}>内容</span>}
|
||||||
extra={<div className={styles.uploadHint}>支持 jpg、png 格式,建议尺寸 800x450px</div>}
|
required
|
||||||
>
|
>
|
||||||
<Upload
|
<div data-color-mode="light">
|
||||||
listType="picture-card"
|
<MDEditor
|
||||||
fileList={fileList}
|
value={mdContent}
|
||||||
onChange={({ fileList }) => setFileList(fileList)}
|
onChange={setMdContent}
|
||||||
beforeUpload={(file) => {
|
height={400}
|
||||||
const isImage = file.type.startsWith('image/');
|
preview="edit"
|
||||||
if (!isImage) {
|
placeholder="请使用 Markdown 语法详细描述您要分享的内容..."
|
||||||
message.error('只能上传图片文件!');
|
/>
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}}
|
|
||||||
maxCount={1}
|
|
||||||
>
|
|
||||||
{fileList.length < 1 && (
|
|
||||||
<div>
|
|
||||||
<PlusOutlined />
|
|
||||||
<div style={{ marginTop: 8 }}>上传图片</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Upload>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<div className={styles.buttonGroup}>
|
<div className={styles.buttonGroup}>
|
||||||
<Button onClick={() => history.back()}>
|
<Button onClick={() => history.back()}>
|
||||||
取消
|
取消
|
||||||
|
Loading…
x
Reference in New Issue
Block a user