index.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import React, { useEffect, useState, useReducer } from 'react';
  2. import { PageContainer } from '@ant-design/pro-layout';
  3. import { Row, Col, Space, Button, Input, Table, Card, Select, message, Tooltip, Image } from 'antd';
  4. import { getPlate, getPosts, enablePosts } from '@/services/tieba';
  5. import { useClassify } from '@/hooks/tieba/index';
  6. import type { initstateType, actionType } from './data';
  7. import DetailModal from './modal';
  8. const request = (url: string): Promise<any> => {
  9. return new Promise((resolve, reject) => {
  10. const xhr = new XMLHttpRequest();
  11. xhr.open('GET', url, false);
  12. xhr.onreadystatechange = function () {
  13. if (xhr.readyState === 4 && xhr.status === 200) {
  14. resolve(xhr.response);
  15. } else {
  16. reject(new Error('置换contenturl出错'));
  17. }
  18. };
  19. xhr.send();
  20. }).catch((e) => e);
  21. };
  22. const sendRequest = async (url: string) => await request(url);
  23. const initstate: initstateType = {
  24. curPage: 1,
  25. barId: '',
  26. categoryId: '',
  27. label: '',
  28. type: 0,
  29. };
  30. const reduce: React.Reducer<initstateType, actionType> = (state, action): initstateType => {
  31. const { type, value } = action;
  32. switch (type) {
  33. case 'reload':
  34. return { ...state, barId: '', categoryId: '', label: '' };
  35. case 'barId':
  36. return { ...state, barId: value };
  37. case 'curPage':
  38. return { ...state, curPage: value };
  39. case 'categoryId':
  40. return { ...state, categoryId: value };
  41. case 'label':
  42. return { ...state, label: value };
  43. case 'type':
  44. return { ...state, type: value };
  45. default:
  46. throw new Error('posts/index.tsx => reduce 参数 action 下, type类型不存在');
  47. }
  48. };
  49. const Posts: React.FC = () => {
  50. const [plateList, setPlateList] = useState([]);
  51. const [postsList, setPostsList] = useState([]);
  52. const classIfy = useClassify();
  53. const [total, setTotal] = useState(0);
  54. const [queryParams, dispatch] = useReducer(reduce, initstate);
  55. const [visible, setVissible] = useState(false);
  56. const [detailData, setDetailData] = useState({});
  57. const [loading, setLoading] = useState(false);
  58. // 当前点击的id
  59. // const [id, setId] = useState('')
  60. // 获取贴吧板块
  61. const GetPlate = async (): Promise<void> => {
  62. const { code, data } = await getPlate({ curPage: 1, pageSize: 10000 });
  63. if (code === 0) {
  64. const { records } = data;
  65. setPlateList(records);
  66. }
  67. };
  68. // 获取帖子
  69. const GetPosts = async (): Promise<void> => {
  70. setLoading(true);
  71. const { code, data } = await getPosts(queryParams);
  72. setLoading(false);
  73. if (code === 0) {
  74. const { records, total: Total } = data;
  75. // eslint-disable-next-line no-plusplus
  76. for (let i = 0; i < records.length; i++) {
  77. // eslint-disable-next-line no-await-in-loop
  78. // records[i].content = records[i].contentUrl && await sendRequest(records[i]?.contentUrl)
  79. }
  80. console.log(records, 'recordsrecordsrecords');
  81. setPostsList(records);
  82. setTotal(Total);
  83. }
  84. };
  85. // 封禁帖子
  86. const EnablePosts = async (record: Record<string, any>): Promise<void> => {
  87. const $par = {
  88. id: record.id,
  89. enable: record.enable ? 0 : 1,
  90. type: queryParams.type,
  91. };
  92. const { code } = await enablePosts($par);
  93. if (code === 0) {
  94. message.success('操作成功');
  95. }
  96. GetPosts();
  97. };
  98. useEffect(() => {
  99. GetPlate();
  100. GetPosts();
  101. }, []);
  102. // 打开弹窗
  103. const openModal = (record: Record<string, any>): void => {
  104. setVissible(true);
  105. setDetailData(record);
  106. };
  107. // 关闭弹窗
  108. const closeModal = (): void => setVissible(false);
  109. // 选择barId
  110. const changeBarId = (value: string): void => dispatch({ type: 'barId', value });
  111. // 填写标题
  112. const changeLabel = (e: React.ChangeEvent<HTMLInputElement>): void =>
  113. dispatch({ type: 'label', value: e.target.value });
  114. return (
  115. <PageContainer title="帖子管理">
  116. <Card>
  117. <Row gutter={[16, 16]} justify="space-between">
  118. <Col>
  119. <Space>
  120. <Select
  121. style={{ width: 200 }}
  122. placeholder="请选择贴吧板块"
  123. value={queryParams.barId || undefined}
  124. onChange={changeBarId}
  125. allowClear
  126. >
  127. {plateList.length &&
  128. plateList.map((item: Record<string, any>) => (
  129. <Select.Option key={item.id} value={item.id}>
  130. {item.label}
  131. </Select.Option>
  132. ))}
  133. </Select>
  134. <Select
  135. style={{ width: 200 }}
  136. placeholder="请选择分类"
  137. value={queryParams.categoryId || undefined}
  138. onChange={(value: string) => dispatch({ type: 'categoryId', value })}
  139. allowClear
  140. >
  141. {classIfy.length &&
  142. classIfy.map((item: Record<string, any>) => (
  143. <Select.Option key={item.id} value={item.id}>
  144. {item.label}
  145. </Select.Option>
  146. ))}
  147. </Select>
  148. <Input onChange={changeLabel} placeholder="请输入帖子标题"></Input>
  149. <Select
  150. style={{ width: 200 }}
  151. placeholder="请选择类型"
  152. value={queryParams.type}
  153. onChange={(value: number) => dispatch({ type: 'type', value })}
  154. allowClear
  155. >
  156. <Select.Option value={0}>帖子</Select.Option>
  157. <Select.Option value={1}>问答</Select.Option>
  158. </Select>
  159. </Space>
  160. </Col>
  161. <Col>
  162. <Space>
  163. <Button onClick={() => dispatch({ type: 'reload' })}>重置</Button>
  164. <Button type="primary" onClick={GetPosts}>
  165. 搜索
  166. </Button>
  167. </Space>
  168. </Col>
  169. </Row>
  170. <Table
  171. rowKey={(record) => record.id}
  172. loading={loading}
  173. style={{ marginTop: 20 }}
  174. dataSource={postsList}
  175. pagination={{ total, onChange: (page) => dispatch({ type: 'curPage', value: page }) }}
  176. >
  177. <Table.Column key="label" dataIndex="label" title="帖子标题"></Table.Column>
  178. <Table.Column
  179. key="content"
  180. width={320}
  181. ellipsis
  182. dataIndex="content"
  183. title="内容"
  184. render={(text, record: Record<string, any>) => (
  185. <Tooltip placement="topLeft" title={record.content}>
  186. <span>{record.content}</span>
  187. </Tooltip>
  188. )}
  189. ></Table.Column>
  190. <Table.Column
  191. key="authorName"
  192. dataIndex="authorName"
  193. title="发布人"
  194. render={(text, record: Record<string, any>) => (
  195. <Space>
  196. <Col>{record.authorName}</Col>
  197. <Col>{record.createTime}</Col>
  198. </Space>
  199. )}
  200. />
  201. <Table.Column key="answerNum" dataIndex="answerNum" title="回帖数"></Table.Column>
  202. <Table.Column key="praiseNum" dataIndex="praiseNum" title="点赞数"></Table.Column>
  203. <Table.Column
  204. key="action"
  205. dataIndex="action"
  206. title="操作"
  207. render={(text, record: Record<string, any>) => (
  208. <Space>
  209. <a onClick={() => openModal(record)}>查看</a>
  210. <a onClick={() => EnablePosts(record)}>{record.enable ? '封禁' : '解封'}</a>
  211. </Space>
  212. )}
  213. />
  214. </Table>
  215. </Card>
  216. {/* 详情弹窗 */}
  217. <DetailModal detailData={detailData} visible={visible} closeModal={closeModal} />
  218. </PageContainer>
  219. );
  220. };
  221. export default Posts;