完成留言管理功能

This commit is contained in:
宇阳
2024-09-28 16:15:10 +08:00
parent 189c30f587
commit de2c20a006
7 changed files with 258 additions and 19 deletions

37
src/api/Wall.ts Normal file
View File

@@ -0,0 +1,37 @@
import Request from '@/utils/request'
import { Wall, Cate } from '@/types/app/wall'
// 新增留言
export const addWallDataAPI = (data: Wall) => Request<Wall>("POST", "/wall", { data })
// 删除留言
export const delWallDataAPI = (id: number) => Request<Wall>("DELETE", `/wall/${id}`)
// 审核留言
export const auditWallDataAPI = (id: number) => Request<Wall>("PATCH", `/wall/audit/${id}`)
// 修改留言
export const editWallDataAPI = (data: Wall) => Request<Wall>("PATCH", "/wall", { data })
// 获取留言
export const getWallDataAPI = (id?: number) => Request<Paginate<Wall>>("GET", `/wall/${id}`)
// 获取留言列表
export const getWallListAPI = (data?: QueryData) => Request<Wall[]>("POST", `/wall/list`, {
data: { ...data?.query },
params: {
sort: data?.sort,
}
})
// 分页获取留言列表
export const getWallPagingAPI = (data?: QueryData) => Request<Paginate<Wall[]>>("POST", `/wall/paging`, {
data: { ...data?.query },
params: {
sort: data?.sort,
...data?.pagination
}
})
// 获取留言分类列表
export const getWallCateListAPI = () => Request<Cate[]>("GET", `/wall/cate`)

View File

@@ -7,6 +7,7 @@ import Create from '@/pages/Create';
import Cate from '@/pages/Cate';
import Article from '@/pages/Article';
import Comment from '@/pages/Comment';
import Wall from "@/pages/Wall";
import Tag from '@/pages/Tag';
import Web from '@/pages/Web';
import Swiper from '@/pages/Swiper';
@@ -41,6 +42,7 @@ export default () => {
{ path: "/article", title: "文章管理", component: <Article /> },
{ path: "/tag", title: "标签管理", component: <Tag /> },
{ path: "/comment", title: "评论管理", component: <Comment /> },
{ path: "/wall", title: "评论管理", component: <Wall /> },
{ path: "/web", title: "网站管理", component: <Web /> },
{ path: "/swiper", title: "轮播图管理", component: <Swiper /> },
{ path: "/footprint", title: "足迹管理", component: <Footprint /> },

View File

@@ -127,6 +127,11 @@ const Sidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => {
path: "comment",
name: "评论管理"
},
{
to: "/wall",
path: "wall",
name: "留言管理"
},
{
to: "/cate",
path: "cate",

View File

@@ -126,11 +126,11 @@ const ArticlePage = () => {
const onSubmit = async (values: FilterForm) => {
const query: FilterArticle = {
key: values.title ? values.title : null,
startDate: values.createTime ? values.createTime[0].valueOf() + '' : null,
endDate: values.createTime ? values.createTime[1].valueOf() + '' : null,
cateIds: values.cateIds ? values.cateIds : null,
tagId: values.tagId ? values.tagId + "" : null,
key: values.title,
startDate: values.createTime ? values.createTime[0].valueOf() + '' : undefined,
endDate: values.createTime ? values.createTime[1].valueOf() + '' : undefined,
cateIds: values.cateIds,
tagId: values.tagId ? values.tagId + "" : undefined,
}
const { data } = await getArticleListAPI({ query });

View File

@@ -43,26 +43,21 @@ const CommentPage = () => {
key: 'id',
align: "center"
},
{
title: '状态',
dataIndex: 'auditStatus',
key: 'auditStatus',
fixed: 'left',
render: (status: number) => status ?
<Tag bordered={false} color="processing"></Tag>
: <Tag bordered={false} color="error"></Tag>
},
// {
// title: '状态',
// dataIndex: 'auditStatus',
// key: 'auditStatus',
// fixed: 'left',
// render: (status: number) => status ?
// <Tag bordered={false} color="processing">通过</Tag>
// : <Tag bordered={false} color="error">待审核</Tag>
// },
{
title: '名称',
dataIndex: 'name',
key: 'name',
fixed: 'left',
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
},
{
title: '内容',
dataIndex: 'content',
@@ -73,6 +68,12 @@ const CommentPage = () => {
setIsModalOpen(true)
}}>{text}</span>
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
render: (text: string) => text ? text : '暂无邮箱',
},
{
title: '网站',
dataIndex: 'url',

177
src/pages/Wall/index.tsx Normal file
View File

@@ -0,0 +1,177 @@
import { useState, useEffect } from 'react';
import { Card, message, Table, Popconfirm, Button, Tag, Modal, Form, Input, DatePicker, Select } from 'antd';
import { getWallListAPI, delWallDataAPI, getWallCateListAPI } from '@/api/Wall';
import { ColumnsType } from 'antd/es/table';
import { titleSty } from '@/styles/sty';
import Title from '@/components/Title';
import { Cate, Wall } from '@/types/app/wall';
import dayjs from 'dayjs';
const WallPage = () => {
const [loading, setLoading] = useState(false);
const [wall, setWall] = useState<Wall>();
const [list, setList] = useState<Wall[]>([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const getWallList = async () => {
const { data } = await getWallListAPI();
// 根据时间排序:最新时间在前
// const sortedData = (data as Wall[]).sort((a, b) => +b.createTime - +a.createTime);
setList(data)
setLoading(false)
}
const delWallData = async (id: number) => {
setLoading(true)
await delWallDataAPI(id);
getWallList();
message.success('🎉 删除留言成功');
};
// 获取留言的分类列表
const [cateList, setCateList] = useState<Cate[]>([])
const getCateList = async () => {
const { data } = await getWallCateListAPI()
setCateList((data as Cate[]).filter(item => item.id !== 1))
}
useEffect(() => {
setLoading(true)
getWallList();
getCateList()
}, []);
const columns: ColumnsType = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
align: "center"
},
{
title: '分类',
dataIndex: 'cate',
key: 'cate',
render: ({ name }, { color }) => <Tag bordered={false} color={color} className='!text-[#565656]'>{name}</Tag>,
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
fixed: 'left',
},
{
title: '内容',
dataIndex: 'content',
key: 'content',
width: 400,
render: (text: string, record) => <span className="hover:text-primary cursor-pointer line-clamp-2" onClick={() => {
setWall(record)
setIsModalOpen(true)
}}>{text}</span>
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
render: (text: string) => text ? text : '暂无邮箱',
},
{
title: '留言时间',
dataIndex: 'createTime',
key: 'createTime',
render: (date: string) => dayjs(+date).format('YYYY-MM-DD HH:mm:ss'),
},
{
title: '操作',
key: 'action',
fixed: 'right',
align: 'center',
render: (text: string, record: Wall) => (
<div className='flex justify-center space-x-2'>
<Button onClick={() => {
setWall(record)
setIsModalOpen(true)
}}></Button>
<Popconfirm title="警告" description="你确定要删除吗" okText="确定" cancelText="取消" onConfirm={() => delWallData(record.id)}>
<Button type="primary" danger></Button>
</Popconfirm>
</div>
),
},
];
const { RangePicker } = DatePicker;
const onSubmit = async (values: FilterForm) => {
const query: FilterData = {
key: values.title ? values.title : undefined,
content: values.content ? values.content : undefined,
startDate: values.createTime ? values.createTime[0].valueOf() + '' : undefined,
endDate: values.createTime ? values.createTime[1].valueOf() + '' : undefined,
}
const { data } = await getWallListAPI({ query });
console.log(data);
setList(data)
}
return (
<>
<Title value='留言管理' />
<Card className='my-2 overflow-scroll'>
<Form layout="inline" onFinish={onSubmit} autoComplete="off" className='flex-nowrap'>
<Form.Item label="内容" name="content" className='w-2/12'>
<Input placeholder='请输入内容关键词' />
</Form.Item>
<Form.Item label="分类" name="cateId" className='w-2/12'>
<Select
allowClear
options={cateList}
fieldNames={{ label: 'name', value: 'id' }}
placeholder="请选择分类"
/>
</Form.Item>
<Form.Item label="时间范围" name="createTime" className='w-3/12'>
<RangePicker placeholder={["选择起始时间", "选择结束时间"]} />
</Form.Item>
<Form.Item className='pr-6'>
<Button type="primary" htmlType="submit"></Button>
</Form.Item>
</Form>
</Card>
<Card className={`${titleSty} mt-2`}>
<Table
rowKey="id"
dataSource={list}
columns={columns}
loading={loading}
expandable={{ defaultExpandAllRows: true }}
scroll={{ x: 'max-content' }}
pagination={{
position: ['bottomCenter'],
defaultPageSize: 8,
}}
/>
</Card>
<Modal title='留言详情' open={isModalOpen} onCancel={() => setIsModalOpen(false)} footer={null}>
<div className='pt-2 space-y-2'>
<div><b></b> {dayjs(wall?.createTime).format("YYYY-MM-DD HH:mm:ss")}</div>
<div><b></b> {wall?.name}</div>
<div><b></b> {wall?.content}</div>
</div>
</Modal>
</>
);
};
export default WallPage;

17
src/types/app/wall.d.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
export interface Cate {
id: number;
name: string;
mark: string;
order: number;
}
export interface Wall {
id: number;
name: string;
cateId: number;
cate: Category;
color: string;
content: string;
auditStatus: number;
createTime: string;
}