index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { QuestionCircleOutlined } from "@ant-design/icons";
  2. import { useDebounceFn, useFullscreen, useLocalStorageState, useMount, useSetState } from "ahooks";
  3. import { Card, Col, Row, Space, Tooltip } from "antd";
  4. import React, { useCallback, useEffect, useRef, useState } from "react"
  5. import style from './index.less'
  6. import Settings from "../Settings";
  7. import CustomListModel from "@/components/CustomList";
  8. import NewTable from "./newTable";
  9. const log = (text?: any, key?: string) => {
  10. console.log(`pro_${key || ''}----->`, text)
  11. }
  12. export const DispatchContext = React.createContext<PROAPI.TableContentProps | null>(null);
  13. export const DispatchHeader = React.createContext<PROAPI.HeaderContentProps | null>(null);
  14. export const version = '1.0.0'
  15. const ran = Math.ceil(Math.random() * 100)
  16. /**
  17. * 升级版表格
  18. * @returns
  19. */
  20. const TablePro: React.FC<PROAPI.TableProProps> = ({
  21. configName,
  22. config,
  23. fixed = { left: 0, right: 0 },
  24. title,
  25. tips,
  26. ajax,
  27. czChild,
  28. leftChild,
  29. size = 'small',
  30. className,
  31. total,
  32. page,
  33. pageSize,
  34. scroll,
  35. dataSource,
  36. isZj,
  37. totalData = [],
  38. summary,
  39. ...props
  40. }) => {
  41. /*********************************************/
  42. const [lsDataSource, setLsDataSource] = useLocalStorageState<PROAPI.ColumnsTypePro<any>>(`myAdMonitorConfig${version}_` + configName);
  43. const [lsFixed] = useLocalStorageState<{ left: number, right: number }>(`myAdMonitorConfigFixed${version}_` + configName);
  44. const [state, setState] = useSetState<PROAPI.State>({
  45. // 所有配置用key转Object
  46. configObj: {},
  47. columns: [],
  48. // 默认配置
  49. defaultColumns: []
  50. });
  51. const ref = useRef(null)
  52. const [isFull, { toggleFull }] = useFullscreen(ref);
  53. const [isFullscreen, setIsFullscreen] = useState<boolean>(true);
  54. const [visible, setVisible] = useState<boolean>(false)
  55. // const { configObj, columns, defaultColumns } = state
  56. /*********************************************/
  57. // 首次渲染
  58. useMount(() => {
  59. log('----mount-----')
  60. handleConfig()
  61. });
  62. /**
  63. * 处理config成对象 减少后期轮询
  64. * 拿到默认Columns
  65. */
  66. const handleConfig = () => {
  67. // log(config)
  68. let newColumns: PROAPI.ColumnsTypePro<any> = []
  69. let newConfigObj: { [key: string]: PROAPI.ColumnTypePro<any>; } = {}
  70. config?.forEach((item: { data: { default: any; dataIndex: string }[] }) => {
  71. item?.data?.forEach((d: { default: any, dataIndex: string }) => {
  72. newConfigObj[d.dataIndex] = { ...d }
  73. if (d.default) {
  74. newColumns[d.default - 1] = d
  75. }
  76. })
  77. })
  78. setState({ defaultColumns: newColumns, configObj: newConfigObj })
  79. handleColumns(newColumns, newConfigObj, lsDataSource, lsFixed)
  80. }
  81. // 处理columns
  82. const handleColumns = (defaultColumns: PROAPI.ColumnsTypePro<any>, configObj: { [key: string]: PROAPI.ColumnTypePro<any>; }, lsDataSource?: PROAPI.ColumnsTypePro<any>, lsFixed?: { left: number, right: number }) => {
  83. // log(defaultColumns, 'defaultColumns')
  84. // log(configObj, 'configObj')
  85. // log(lsDataSource, 'lsDataSource')
  86. // 使用默认配置
  87. let newColumns: PROAPI.ColumnsTypePro<any> = defaultColumns
  88. let newFixed: { left: number, right: number } = fixed
  89. if (lsFixed) {
  90. newFixed = lsFixed
  91. }
  92. if (lsDataSource) { // 找到了设置的配置
  93. newColumns = lsDataSource
  94. .filter((item: { dataIndex: string | number; }) => !!item && configObj[item.dataIndex])
  95. .map((item: { dataIndex: string | number; width: number }) => {
  96. let column = { ...configObj[item.dataIndex] }
  97. column['width'] = item.width
  98. return column
  99. })
  100. }
  101. // log(newFixed, 'newFixed')
  102. newColumns = newColumns.map(item => {
  103. if (item?.tips && typeof item.title === 'string') {
  104. let column: any = configObj[item.dataIndex]
  105. item.title = <Space size={2}>
  106. {column.title}
  107. <Tooltip title={column?.tips} placement='bottom'>
  108. <QuestionCircleOutlined />
  109. </Tooltip>
  110. </Space>
  111. }
  112. return { ...item, key: item.dataIndex }
  113. })
  114. if (newFixed.left || newFixed.right) {
  115. for (let i = 0; i < Math.min(newFixed.left, newColumns.length); i++) {
  116. newColumns[i] = { ...newColumns[i], fixed: 'left' };
  117. }
  118. const arrayLength = newColumns.length;
  119. for (let i = Math.max(0, arrayLength - newFixed.right); i < arrayLength; i++) {
  120. newColumns[i] = { ...newColumns[i], fixed: 'right' };
  121. }
  122. }
  123. // log(newColumns, 'newColumns')
  124. setState({ columns: newColumns })
  125. }
  126. useEffect(() => {
  127. const contentBodyScroll = (e: any) => {
  128. let el = document.querySelector(`.header_table_body_${ran} .ant-table-body`);
  129. if (el) {
  130. el.scrollLeft = e.target.scrollLeft
  131. }
  132. }
  133. const headerBodyScroll = (e: any) => {
  134. let el = document.querySelector(`.content_table_body_${ran} .ant-table-body`);
  135. if (el) {
  136. el.scrollLeft = e.target.scrollLeft
  137. }
  138. }
  139. if (isZj) {
  140. document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', contentBodyScroll);
  141. document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.addEventListener('scroll', headerBodyScroll);
  142. }
  143. () => {
  144. if (isZj) {
  145. document.querySelector(`.content_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', contentBodyScroll);
  146. document.querySelector(`.header_table_body_${ran} .ant-table-body`)?.removeEventListener('scroll', headerBodyScroll);
  147. }
  148. }
  149. }, [isZj, ran])
  150. const { run: runResize } = useDebounceFn((columns) => {
  151. if (configName) {
  152. let newSelectData: PROAPI.ColumnsTypePro<any> = []
  153. state.columns?.forEach((item, index) => {
  154. newSelectData.push({ ...JSON.parse(JSON.stringify(state.configObj[item.dataIndex])), width: columns[index]['width'] })
  155. })
  156. setLsDataSource(newSelectData)
  157. if (isZj) { // 有总计需要刷新内容表格
  158. handleColumns(state.defaultColumns, state.configObj, newSelectData, lsFixed)
  159. }
  160. }
  161. }, { wait: 200 });
  162. //拖动宽度设置设置保存
  163. const handelResize = useCallback((columns: PROAPI.ColumnTypePro<any>) => {
  164. runResize(columns)
  165. }, [configName, state.columns])
  166. return <>
  167. {/* 设置展示参数 */}
  168. {visible && <CustomListModel
  169. columns={state.columns}
  170. sysFixed={fixed}
  171. version={version}
  172. config={config}
  173. configName={configName}
  174. visible={visible}
  175. onClose={() => { setVisible(false) }}
  176. onChange={(value) => {
  177. if (value) {
  178. handleColumns(state.defaultColumns, state.configObj, value?.selectData, value?.fixed)
  179. } else {
  180. handleColumns(state.defaultColumns, state.configObj)
  181. }
  182. }}
  183. />}
  184. <Row gutter={[0, 20]} ref={ref} style={isFull ? { background: '#fff' } : {}}>
  185. <Col span={24}>
  186. <Card
  187. style={{ borderRadius: 8 }}
  188. headStyle={{ textAlign: 'left' }}
  189. bodyStyle={{ padding: '5px 10px' }}
  190. >
  191. {title && <div className={style.title}>
  192. <Space><span>{title}</span>{tips && <Tooltip title={tips}><span><QuestionCircleOutlined /></span></Tooltip>}</Space>
  193. </div>}
  194. <Row gutter={[0, 16]}>
  195. <DispatchHeader.Provider value={{ setIsFullscreen, isFullscreen, isFull, toggleFull, setVisible, ajax, czChild, leftChild }}>
  196. <Settings />
  197. </DispatchHeader.Provider>
  198. <DispatchContext.Provider value={{ total, page, pageSize, handelResize }}>
  199. <Col span={24}>
  200. <div className={`${style[size]} ${className ? style[className] : ''} `}>
  201. {
  202. isZj && <NewTable
  203. bordered
  204. columns={state.columns}
  205. dataSource={totalData?.length > 0 ? [...totalData] : [{ id: 1 }]}
  206. scroll={scroll ? isFull ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
  207. size={size}
  208. pagination={false}
  209. className={`all_table header_table_body header_table_body_${ran} ${className ? className : ''}`}
  210. sortDirections={['ascend', 'descend', null]}
  211. {...props}
  212. />
  213. }
  214. <NewTable
  215. showHeader={!isZj}
  216. sortDirections={['ascend', 'descend', null]}
  217. bordered
  218. className={`all_table content_table_body content_table_body_${ran} ${className ? className : ''}`}
  219. scroll={scroll ? isFull ? { ...scroll, y: document.body.clientHeight - 300 } : scroll : {}}
  220. columns={state.columns}
  221. dataSource={dataSource}
  222. summary={summary}
  223. pagination={{}}
  224. {...props}
  225. />
  226. </div>
  227. </Col>
  228. </DispatchContext.Provider>
  229. </Row>
  230. </Card>
  231. </Col>
  232. </Row>
  233. </>
  234. }
  235. export default React.memo(TablePro)