index.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import React, { useEffect, useState } from 'react';
  2. import { Space, Table, Button, Input, Select, Tabs, message } from 'antd';
  3. import type { TableProps } from 'antd';
  4. import styles from './index.module.css';
  5. import { WeComPlan, WeComPlanListResponse, WeComPlanType, WeVideoItem } from './type';
  6. import request from '@src/http/index';
  7. import { getQwPlanListApi, getShareQrPic, saveQwPlanApi } from "@src/http/api"
  8. import VideoSelectModal from './components/videoSelectModal';
  9. import LinkDetailModal from './components/linkDetailModal';
  10. import PlanDetailModal from './components/planDetailModal';
  11. import http from '@src/http/index';
  12. import copy from 'copy-to-clipboard';
  13. import VideoPlayModal from './components/videoPlayModal';
  14. import modal from 'antd/es/modal';
  15. // Define a type for the expected API response (adjust if needed based on actual API)
  16. const TableHeight = window.innerHeight - 380;
  17. const WeGZHContent: React.FC = () => {
  18. // 状态管理
  19. const [videoTitle, setVideoTitle] = useState<string>('');
  20. const [selectedPublisher, setSelectedPublisher] = useState<number>();
  21. const [isShowAddPunlishPlan, setIsShowAddPunlishPlan] = useState<boolean>(false);
  22. const [editPlanData, setEditPlanData] = useState<WeComPlan>();
  23. const [activeKey, setActiveKey] = useState<WeComPlanType>(WeComPlanType.每日推送);
  24. const [tableData, setTableData] = useState<WeComPlan[]>([]);
  25. const [totalSize, setTotalSize] = useState<number>(0);
  26. const [pageNum, setPageNum] = useState<number>(1);
  27. // State for the new modal
  28. const [isLinkDetailModalVisible, setIsLinkDetailModalVisible] = useState<boolean>(false);
  29. const [createdVideoLinks, setCreatedVideoLinks] = useState<WeComPlan[]>([]);
  30. // State for the Plan Detail modal
  31. const [isPlanDetailModalVisible, setIsPlanDetailModalVisible] = useState<boolean>(false);
  32. const [isVideoPlayModalVisible, setIsVideoPlayModalVisible] = useState<boolean>(false);
  33. const getTableData = (_pageNum?: number) => {
  34. setPageNum(_pageNum || 1);
  35. request.post<WeComPlanListResponse>(getQwPlanListApi, {
  36. pageNum: _pageNum || pageNum,
  37. pageSize: 10,
  38. scene: selectedPublisher,
  39. title: videoTitle,
  40. type: +activeKey,
  41. }
  42. ).then(res => {
  43. setTableData(res.data.objs || []);
  44. setTotalSize(res.data.totalSize);
  45. }).catch(err => {
  46. message.error(err.msg || '获取数据失败');
  47. });
  48. }
  49. useEffect(() => {
  50. getTableData(1);
  51. }, [activeKey]);
  52. // 表格列配置
  53. const columns: TableProps<WeComPlan>['columns'] = [
  54. {
  55. title: '创建时间',
  56. dataIndex: 'createTimestamp',
  57. key: 'createTimestamp',
  58. width: 200,
  59. render: (_, record) => {
  60. return record.createTimestamp ? new Date(record.createTimestamp).toLocaleString() : '';
  61. }
  62. },
  63. {
  64. title: '视频标题',
  65. dataIndex: 'title',
  66. key: 'title',
  67. ellipsis: true,
  68. },
  69. {
  70. title: '场景',
  71. dataIndex: 'scene',
  72. key: 'scene',
  73. width: 100,
  74. render: (_, record) => {
  75. if (activeKey === WeComPlanType.每日推送) {
  76. return record.scene === 0 ? '群发' : '单发';
  77. } else {
  78. return '自动回复';
  79. }
  80. }
  81. },
  82. {
  83. title: '操作',
  84. key: 'action',
  85. width: 500,
  86. render: (_, record) => (
  87. <Space size="middle" wrap>
  88. <Button type="link" onClick={() => playVideo(record)}>播放</Button>
  89. <Button type="link" onClick={() => downloadFile(record.cover, record.title + '_cover')}>下载封面</Button>
  90. <Button type="link" onClick={() => showQrCodeModal(record.pageUrl)}>二维码</Button>
  91. <Button type="link" onClick={() => copyToClipboard(record.pageUrl)}>复制链接</Button>
  92. <Button type="link" onClick={() => showDetailModal(record)}>详情</Button>
  93. </Space>
  94. ),
  95. },
  96. ];
  97. const playVideo = (record: WeComPlan) => {
  98. setEditPlanData(record);
  99. setIsVideoPlayModalVisible(true);
  100. }
  101. const showDetailModal = (record: WeComPlan) => {
  102. setEditPlanData(record);
  103. setIsPlanDetailModalVisible(true);
  104. }
  105. const copyToClipboard = (text: string) => {
  106. copy(text);
  107. message.success('复制成功');
  108. };
  109. const downloadFile = (url: string, filename: string) => {
  110. if (!url) {
  111. message.warning('无可用文件下载');
  112. return;
  113. }
  114. const link = document.createElement('a');
  115. link.href = url;
  116. link.download = filename || url.substring(url.lastIndexOf('/') + 1);
  117. link.target = '_blank';
  118. link.rel = 'noopener noreferrer';
  119. document.body.appendChild(link);
  120. link.click();
  121. document.body.removeChild(link);
  122. message.success('开始下载...');
  123. };
  124. const showQrCodeModal = (pageUrl: string) => {
  125. http.get<string>(getShareQrPic, {
  126. params: {
  127. pageUrl,
  128. }
  129. }).then(res => {
  130. modal.info({
  131. title: '二维码',
  132. content: <img src={res.data} alt="二维码" />,
  133. });
  134. }).catch(err => {
  135. message.error(err.msg || '获取二维码失败');
  136. });
  137. };
  138. const addPunlishPlan = () => {
  139. setIsShowAddPunlishPlan(true);
  140. }
  141. const handleOk = (selectedVideos: WeVideoItem[]) => {
  142. http.post<WeComPlan[]>(saveQwPlanApi, {
  143. type: +activeKey,
  144. videoList: selectedVideos,
  145. }).then(res => {
  146. if (res.code === 0) {
  147. message.success('创建成功');
  148. setCreatedVideoLinks(res.data);
  149. setIsLinkDetailModalVisible(true);
  150. setIsShowAddPunlishPlan(false);
  151. getTableData();
  152. } else {
  153. message.error(res.msg || '创建失败');
  154. }
  155. }).catch(err => {
  156. message.error(err.msg || '创建失败');
  157. });
  158. }
  159. return (
  160. <div>
  161. <div className="rounded-lg">
  162. <div className="text-lg font-medium mb-3">企业微信内容库</div>
  163. {/* 搜索区域 */}
  164. <div className="flex flex-wrap gap-4 mb-3">
  165. <div className="flex items-center gap-2">
  166. <span className="text-gray-600">视频标题:</span>
  167. <Input
  168. placeholder="搜索视频标题"
  169. style={{ width: 200 }}
  170. value={videoTitle}
  171. allowClear
  172. onPressEnter={() => getTableData(1)}
  173. onChange={e => setVideoTitle(e.target.value)}
  174. />
  175. </div>
  176. <div className="flex items-center gap-2">
  177. <span className="text-gray-600">场景:</span>
  178. <Select
  179. placeholder="筛选场景"
  180. style={{ width: 200 }}
  181. value={selectedPublisher}
  182. onChange={setSelectedPublisher}
  183. allowClear
  184. options={activeKey === WeComPlanType.每日推送 ? [
  185. { label: '群发', value: 0 },
  186. { label: '单发', value: 1 },
  187. ] : [
  188. { label: '关注回复', value: 0 },
  189. ]}
  190. />
  191. </div>
  192. <Button type="primary" className="ml-2" onClick={() => getTableData(1)}>搜索</Button>
  193. </div>
  194. <Tabs
  195. defaultActiveKey="1"
  196. type="card"
  197. size="large"
  198. items={[
  199. { label: '每日推送', key: WeComPlanType.每日推送 },
  200. { label: '自动回复', key: WeComPlanType.自动回复 },
  201. ]}
  202. activeKey={activeKey}
  203. onChange={(key: string) => setActiveKey(key as WeComPlanType)}
  204. tabBarExtraContent={
  205. { right: <Button type="primary" onClick={addPunlishPlan}>+ 创建发布</Button> }}
  206. />
  207. {/* 表格区域 */}
  208. <Table
  209. rowKey={(record) => record.id}
  210. className={styles.antTable}
  211. columns={columns}
  212. dataSource={tableData}
  213. scroll={{ x: 'max-content', y: TableHeight }}
  214. pagination={{
  215. current: pageNum,
  216. total: totalSize,
  217. pageSize: 10,
  218. showTotal: (total) => `共 ${total} 条`,
  219. onChange: (page) => getTableData(page),
  220. }}
  221. />
  222. <VideoSelectModal
  223. visible={isShowAddPunlishPlan}
  224. onClose={() => {
  225. setIsShowAddPunlishPlan(false);
  226. setEditPlanData(undefined);
  227. }}
  228. onOk={handleOk}
  229. initialSelectedIds={editPlanData ? [editPlanData.id] : []}
  230. planType={activeKey}
  231. />
  232. <LinkDetailModal
  233. visible={isLinkDetailModalVisible}
  234. onClose={() => setIsLinkDetailModalVisible(false)}
  235. videos={createdVideoLinks}
  236. />
  237. <PlanDetailModal
  238. visible={isPlanDetailModalVisible}
  239. onClose={() => {
  240. setIsPlanDetailModalVisible(false);
  241. setEditPlanData(undefined);
  242. }}
  243. planData={editPlanData}
  244. />
  245. <VideoPlayModal
  246. visible={isVideoPlayModalVisible}
  247. onClose={() => setIsVideoPlayModalVisible(false)}
  248. videoUrl={editPlanData?.video || ''}
  249. title={editPlanData?.title || ''}
  250. />
  251. </div>
  252. </div>
  253. );
  254. };
  255. export default WeGZHContent;