index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. import CustomListModel from '@/components/CustomList'
  2. import Tables from '@/components/Tables'
  3. import { quanpin } from '@/utils/fullScreen'
  4. import { FullscreenExitOutlined, FullscreenOutlined, RedoOutlined, SettingOutlined, SyncOutlined } from '@ant-design/icons'
  5. import { Button, Card, Col, Row, Space, Tooltip, } from 'antd'
  6. import { ColumnsType } from 'antd/lib/table'
  7. import React, { useEffect, useRef, useState, useCallback } from 'react'
  8. import { useModel } from 'umi'
  9. import style from './index.less'
  10. interface Prosp {
  11. // sortArr?: { label: string, value: string ,key:any}[],//排序参数名列表
  12. isZj?: boolean,//是否查总计
  13. tableTotal?: { [key: string]: string },//是个开启总计
  14. scroll?: { x?: number, y?: number },//开启行滑动并设置容器最大宽度
  15. columns: () => ColumnsType<any>,//table列表配置
  16. title?: string,//tabel的标题
  17. tooltip?: JSX.Element,//是否在标题后加问号展示说明
  18. dataSource: any[],//table的数据
  19. columnWidth?: string | number
  20. expandedRowRender?: (data: any) => JSX.Element,
  21. className?: string,//自定义class
  22. isdownload?: boolean,
  23. leftChild?: JSX.Element,
  24. config?: any,
  25. configName?: any,
  26. page?: number,
  27. pageSize?: number,
  28. size?: 'small' | 'middle' | 'large',
  29. total?: number,
  30. loading?: boolean,
  31. onChange?: (props: {
  32. pagination?: { current?: number, pageSize?: number, gzh?: string },
  33. filters?: any,
  34. sortData?: {
  35. column: { dataIndex: string },
  36. order?: "ascend" | "descend"
  37. }
  38. }) => void,
  39. ajax?: any,//接口刷新
  40. syncAjax?: any,//同步
  41. hoverable?: boolean,
  42. rowSelection?: any,
  43. myKey?: any,//自定义使用哪个值做key
  44. fixed?: {
  45. left: number,
  46. right: number
  47. },
  48. bodyStyle?: any
  49. gutter?: any
  50. isCard?: boolean
  51. totalData?: any[]
  52. refreshData?: () => void,
  53. rowClassName?: string | ((record: any, index: any) => string),//样式
  54. }
  55. function TableData(props: Prosp) {
  56. const { isZj, scroll, columns, title, columnWidth, rowClassName, dataSource, totalData = [], refreshData, expandedRowRender, className, isCard = true, leftChild, page = undefined, rowSelection = false, pageSize = undefined, size = 'small', fixed = { left: 0, right: 1 }, total = 0, onChange, config, configName, ajax, syncAjax, hoverable = true, myKey, gutter = [0, 20] } = props
  57. const { state: userState } = useModel('useOperating.useUser')
  58. const { isFell } = userState
  59. const [visible, setVisible] = useState<boolean>(false)
  60. const [oldSelectData, setoldSelectData] = useState<any[]>([])
  61. const [oldFixed, setoldFixed] = useState<any>({ left: '0', right: '0' })
  62. const [newColumns, setNewColumns] = useState<any[]>([])
  63. // const [oldConfigName, setOldConfigName] = useState<any>('')//上次的配置名称
  64. const [selectData, setSelectData] = useState<{ selectData: any[], fixed: { left: number, right: number } }>({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
  65. const [tiptopShow, setTipTopShow] = useState({//tiptop开关
  66. ajaxShow: false,
  67. syncAjaxShow: false,
  68. configShow: false,
  69. })
  70. const ref = useRef(null)
  71. const oldName = useRef(null)
  72. const version = '1.0.0'
  73. // // /**重组选中的字段 */
  74. useEffect(() => {
  75. let oldConfigName = oldName.current || ''
  76. if (configName) {
  77. // if (dataSource?.length > 0) {
  78. const defSelectData = localStorage.getItem(`myAdMonitorConfig${version}_` + configName)
  79. const defFixed = localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName)
  80. const newConfig = config?.map((item: { data: any }) => item.data)?.flat()
  81. if (defSelectData && (selectData?.selectData?.length === 0 || configName !== oldConfigName)) {//首次查找个人配置是否存在,并且selectData为空,存在用个人配置设置selectData
  82. console.log('首次使用个人配置赋值')
  83. let newDefSelectData = JSON.parse(defSelectData)
  84. newDefSelectData = newDefSelectData.filter((item: any) => !!item && newConfig.some((c: { dataIndex: any }) => c.dataIndex === item.dataIndex))//去除空项
  85. setSelectData(() => ({ selectData: newDefSelectData, fixed: defFixed ? JSON.parse(defFixed) : { left: 0, right: 0 } }))
  86. }
  87. if (!defSelectData && (selectData?.selectData?.length === 0 || configName !== oldConfigName)) {//首次查找个人配置是否存在,并且selectData为空,不存在默认配置设置selectData
  88. let newSelectData: any[] = []
  89. config?.forEach((item: { data: { default: any }[] }) => {
  90. item?.data?.forEach((d: { default: any }) => {
  91. if (d.default) {
  92. newSelectData[d.default - 1] = d
  93. }
  94. })
  95. })
  96. console.log('首次使用默认配置赋值')
  97. setSelectData(() => ({ ...selectData, selectData: newSelectData }))
  98. }
  99. if ((JSON.stringify(oldSelectData) !== JSON.stringify(selectData?.selectData)) || (JSON.stringify(selectData.fixed) !== JSON.stringify(oldFixed))) {
  100. console.log('设置配置改变重新赋值')
  101. setoldSelectData(() => selectData.selectData)
  102. setoldFixed(() => selectData.fixed)
  103. let newArr: any = []
  104. selectData?.selectData?.forEach((data: { dataIndex: any, width: number }, index: number) => {
  105. columns()?.forEach((item: any) => {
  106. if (data.dataIndex === item.dataIndex) {
  107. if (index < Number(selectData?.fixed?.left)) {//设置左悬浮
  108. item['fixed'] = 'left'
  109. } else if (index > (selectData?.selectData?.length - Number(selectData?.fixed?.right) - 1)) {//设置右悬浮
  110. item['fixed'] = 'right'
  111. } else {
  112. item['fixed'] = false
  113. }
  114. if (data?.width) {
  115. item['width'] = data.width
  116. }
  117. newArr.push(item)
  118. }
  119. })
  120. })
  121. setNewColumns(() => newArr)
  122. }
  123. }
  124. if (configName !== oldConfigName) {
  125. oldName.current = configName
  126. }
  127. }, [selectData, oldSelectData, dataSource, oldFixed, configName, config, columns]) // selectData, oldSelectData, dataSource, oldFixed, configName, oldConfigName, config, columns
  128. //拖动宽度设置设置保存
  129. const handelResize = useCallback((columns: any) => {
  130. if (configName) {
  131. let newSelectData = selectData?.selectData?.map((item, index) => {
  132. item['width'] = columns[index]['width']
  133. return item
  134. })
  135. localStorage.setItem(`myAdMonitorConfig${version}_` + configName, JSON.stringify(newSelectData))
  136. }
  137. }, [configName, selectData])
  138. // 初始展示TIPTOP提示功能
  139. useEffect(() => {
  140. let time: any = null
  141. function timeOut(arg: any) {
  142. return new Promise((res, rel) => {
  143. time = setTimeout(() => {
  144. setTipTopShow({ ...tiptopShow, ...arg })
  145. res(true)
  146. }, 3000)
  147. })
  148. }
  149. async function isShow() {
  150. if (ajax) {
  151. setTipTopShow({ ...tiptopShow, ajaxShow: true })
  152. await timeOut({ ajaxShow: false })
  153. }
  154. if (syncAjax) {
  155. setTipTopShow({ ...tiptopShow, syncAjaxShow: true })
  156. await timeOut({ syncAjaxShow: false })
  157. }
  158. if (config) {
  159. setTipTopShow({ ...tiptopShow, configShow: true })
  160. await timeOut({ configShow: false })
  161. }
  162. }
  163. isShow()
  164. return () => {
  165. clearTimeout(time)
  166. time = null
  167. }
  168. }, [])
  169. const header = <Col span={24}>
  170. <Row gutter={[0, 10]}>
  171. <Col span={24} style={{
  172. display: 'flex',
  173. justifyContent: 'space-between',
  174. alignItems: 'end'
  175. }}>
  176. <Space>
  177. {leftChild}
  178. </Space>
  179. {/*紧凑*/}
  180. <div style={{ float: 'right', display: 'flex', flexFlow: 'row' }}>
  181. {
  182. ajax && <Button
  183. size='small'
  184. type='text'
  185. style={{ display: 'flex', alignItems: 'center' }}
  186. onClick={() => {
  187. ajax.refresh()
  188. }}>
  189. <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{ajax?.data?.reqTime}</span>
  190. <Tooltip
  191. title='刷新'
  192. visible={tiptopShow.ajaxShow}
  193. onVisibleChange={(visible) => {
  194. setTipTopShow({ ...tiptopShow, ajaxShow: visible })
  195. }}
  196. ><RedoOutlined style={{ color: '#2196f3', fontSize: 17 }} /></Tooltip>
  197. </Button>
  198. }
  199. {
  200. syncAjax && <Button
  201. size='small'
  202. type='text'
  203. style={{ display: 'flex', alignItems: 'center' }}
  204. onClick={syncAjax}
  205. >
  206. <Tooltip
  207. title='同步最新'
  208. visible={tiptopShow.syncAjaxShow}
  209. onVisibleChange={(visible) => {
  210. setTipTopShow({ ...tiptopShow, syncAjaxShow: visible })
  211. }}
  212. ><SyncOutlined style={{ color: 'red', fontSize: 17 }} /></Tooltip>
  213. </Button>
  214. }
  215. {config && <Button
  216. size='small'
  217. type='text'
  218. style={{ display: 'flex', alignItems: 'center' }}
  219. onClick={() => {
  220. setVisible(true)
  221. }}>
  222. <Tooltip title='设置'><SettingOutlined /></Tooltip>
  223. </Button>}
  224. <Button
  225. type='text'
  226. size='small'
  227. style={{ display: 'flex', alignItems: 'center' }}
  228. onClick={() => {
  229. if (ref?.current) {
  230. quanpin(ref?.current)
  231. }
  232. }}>
  233. {
  234. <Tooltip title={!isFell ? '全屏' : '退出全屏'}>{!isFell ? <FullscreenOutlined /> : <FullscreenExitOutlined />}</Tooltip>
  235. }
  236. </Button>
  237. {visible && <CustomListModel
  238. sysFixed={fixed}
  239. version={version}
  240. config={config}
  241. configName={configName}
  242. visible={visible}
  243. onClose={() => { setVisible(false) }}
  244. onChange={(arr: any) => {
  245. // window.location.reload()
  246. refreshData?.()
  247. if (arr) {
  248. setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
  249. } else {
  250. setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
  251. }
  252. }}
  253. columns={newColumns} />}
  254. </div>
  255. </Col>
  256. </Row>
  257. </Col>
  258. const content = <Row gutter={[0, 20]} ref={ref} style={isFell ? { background: '#fff' } : {}}>
  259. {/**table */}
  260. <Col span={24}>
  261. {isCard ? <Card
  262. hoverable={hoverable}
  263. title={title}
  264. bodyStyle={props?.bodyStyle}
  265. headStyle={{ textAlign: 'left' }}
  266. >
  267. <Row gutter={gutter}>
  268. {header}
  269. <Tab {...{ size, newColumns, rowClassName, handelResize, columnWidth, className, isZj, totalData, rowSelection, columns, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
  270. </Row>
  271. </Card> : <Row gutter={gutter}>
  272. {header}
  273. <Tab {...{ size, newColumns, rowClassName, handelResize, className, isZj, columnWidth, totalData, rowSelection, columns, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
  274. </Row>}
  275. </Col>
  276. </Row >
  277. return <>
  278. {content}
  279. </>
  280. }
  281. /**表格 */
  282. const Tab = React.memo((props: any) => {
  283. const { size, newColumns, rowClassName, className, handelResize, isZj, columns, scroll, columnWidth, totalData, rowSelection, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey } = props
  284. let ran = Math.ceil(Math.random() * 100)
  285. useEffect(() => {
  286. const contentBodyScroll = (e: any) => {
  287. let el = document.querySelector(`.header_table_body_${ran} .ant-table-body`);
  288. if (el) {
  289. el.scrollLeft = e.target.scrollLeft
  290. }
  291. }
  292. const headerBodyScroll = (e: any) => {
  293. let el = document.querySelector(`.content_table_body_${ran} .ant-table-body`);
  294. if (el) {
  295. el.scrollLeft = e.target.scrollLeft
  296. }
  297. }
  298. if (isZj) {
  299. document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', contentBodyScroll);
  300. document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', headerBodyScroll);
  301. }
  302. () => {
  303. if (isZj) {
  304. document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', contentBodyScroll);
  305. document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', headerBodyScroll);
  306. }
  307. }
  308. }, [isZj, ran])
  309. return < Col span={24} >
  310. <div className={`${style[size]} ${className ? style[className] : ''} `}>
  311. {
  312. isZj && <Tables
  313. bordered
  314. columns={newColumns}
  315. dataSource={totalData?.length > 0 ? [...totalData] : [{ id: 1 }]}
  316. scroll={scroll ? isFell ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
  317. size={size}
  318. pagination={false}
  319. hideOnSinglePage
  320. current={page}
  321. pageSize={pageSize}
  322. loading={ajax?.loading}
  323. rowSelection={rowSelection}
  324. className={`all_table header_table_body header_table_body_${ran} ${className ? className : ''}`}
  325. sortDirections={['ascend', 'descend', null]}
  326. handelResize={((columns: any) => handelResize(columns, 'antd'))}
  327. onChange={(pagination: any, filters: any, sorter: any) => onChange && onChange({ pagination, filters, sortData: sorter })}
  328. />
  329. }
  330. <Tables
  331. showHeader={!isZj}
  332. className={`all_table content_table_body_${ran} ${className ? className : ''}`}
  333. bordered
  334. sortDirections={['descend', 'ascend', null]}
  335. current={page}
  336. pageSize={pageSize}
  337. columns={newColumns?.length > 0 ? newColumns : columns()}
  338. dataSource={dataSource}
  339. scroll={scroll ? isFell ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
  340. onChange={(pagination: any, filters: any, sorter: any) => onChange && onChange({ pagination, filters, sortData: sorter })}
  341. rowClassName={rowClassName}
  342. columnWidth={columnWidth}
  343. expandedRowRender={expandedRowRender ? expandedRowRender : undefined}
  344. size={size}
  345. total={total}
  346. loading={ajax.loading}
  347. defaultPageSize={20}
  348. rowSelection={rowSelection}
  349. handelResize={((columns: any) => handelResize(columns))}
  350. myKey={myKey}
  351. />
  352. </div>
  353. </Col >
  354. })
  355. export default React.memo(TableData)