index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
  2. import styles from './index.module.scss'
  3. import { Button, Cascader, Checkbox, Input, Popconfirm, Select, Table } from 'antd'
  4. import { useDispatch, useSelector } from 'react-redux'
  5. import { A2FromDataType, options1 } from './data'
  6. import {
  7. A2_APIgetCity,
  8. A2_APIgetlist,
  9. A2_APIgetlistDerive,
  10. A2_APIremoves
  11. } from '@/store/action/A2Psychz'
  12. import { RootState } from '@/store'
  13. import { A2tableType } from '@/types'
  14. import { MessageFu } from '@/utils/message'
  15. import { A1addType } from '../A1Camera/data'
  16. import AddPsychz from './AddPsychz'
  17. import dayjs from 'dayjs'
  18. import ExportJsonExcel from 'js-export-excel'
  19. const baseTableSelect: A2FromDataType = {
  20. siteArr: undefined,
  21. province: '',
  22. city: '',
  23. region: '',
  24. searchKey: '',
  25. pmUser: '',
  26. typeIn: '',
  27. pageSize: 10,
  28. pageNum: 1,
  29. isSiteEmpty: 0
  30. }
  31. function A2Psychz() {
  32. // 站址地区的数据-下拉框
  33. const [optionsCity, setOptionsCity] = useState<any>([])
  34. const getCityFu = useCallback(async () => {
  35. const res = await A2_APIgetCity()
  36. if (res.code === 0) {
  37. const obj = res.data
  38. const arr = []
  39. for (const k in obj) {
  40. const temp1: any = {
  41. value: k,
  42. label: k,
  43. children: []
  44. }
  45. const children1Obj = Reflect.get(obj, k)
  46. for (const k2 in children1Obj) {
  47. const children2Arr = Reflect.get(children1Obj, k2)
  48. const objTemp = {
  49. value: k2,
  50. label: k2,
  51. children: children2Arr.map((v2: any) => ({
  52. value: v2.region,
  53. label: v2.region
  54. }))
  55. }
  56. temp1.children.push(objTemp)
  57. }
  58. arr.push(temp1)
  59. }
  60. setOptionsCity(arr)
  61. }
  62. }, [])
  63. useEffect(() => {
  64. getCityFu()
  65. }, [getCityFu])
  66. const dispatch = useDispatch()
  67. // 筛选和分页
  68. const [tableSelect, setTableSelect] = useState(baseTableSelect)
  69. const tableSelectRef = useRef({} as A2FromDataType)
  70. useEffect(() => {
  71. tableSelectRef.current = { ...tableSelect }
  72. }, [tableSelect])
  73. // 点击搜索的 时间戳
  74. const [timeKey, setTimeKey] = useState(-1)
  75. // 点击搜索
  76. const clickSearch = useCallback(() => {
  77. setTableSelect({ ...tableSelect, pageNum: 1 })
  78. setTimeout(() => {
  79. setTimeKey(Date.now())
  80. }, 50)
  81. }, [tableSelect])
  82. // 发送接口的函数
  83. const getListFu = useCallback(() => {
  84. const objTemp: any = {}
  85. if (tableSelectRef.current.siteArr) {
  86. const temp = tableSelectRef.current.siteArr
  87. objTemp.province = temp[0] || ''
  88. objTemp.city = temp[1] || ''
  89. objTemp.region = temp[2] || ''
  90. }
  91. const obj = {
  92. ...tableSelectRef.current,
  93. ...objTemp
  94. }
  95. dispatch(A2_APIgetlist(obj))
  96. }, [dispatch])
  97. useEffect(() => {
  98. // 从进度统计进来------带入参数
  99. const urlStr = decodeURI(window.location.href)
  100. if (urlStr.includes('?C=')) {
  101. const path = urlStr.split('?C=')[1]
  102. const pathArr = path.split('/')
  103. const obj: A2FromDataType = {
  104. ...baseTableSelect,
  105. siteArr: pathArr
  106. }
  107. setTableSelect(obj)
  108. // 注意 这里有个坑。 setTableSelect 不能直接设置 province 和 city
  109. tableSelectRef.current = { ...obj, province: pathArr[0], city: pathArr[1] || '' }
  110. }
  111. if (urlStr.includes('?N=')) {
  112. const obj: A2FromDataType = {
  113. ...baseTableSelect,
  114. isSiteEmpty: 1
  115. }
  116. setTableSelect(obj)
  117. tableSelectRef.current = obj
  118. }
  119. getListFu()
  120. }, [getListFu])
  121. useEffect(() => {
  122. if (timeKey !== -1) getListFu()
  123. }, [getListFu, timeKey])
  124. // 输入框的改变
  125. const txtChangeFu = useCallback(
  126. (txt: string, key: 'pmUser' | 'searchKey') => {
  127. setTableSelect({ ...tableSelect, [key]: txt })
  128. },
  129. [tableSelect]
  130. )
  131. // 点击重置
  132. const [inputKey, setInputKey] = useState(1)
  133. const resetSelectFu = useCallback(() => {
  134. // 把2个输入框和时间选择器清空
  135. setInputKey(Date.now())
  136. setTableSelect(baseTableSelect)
  137. setTimeout(() => {
  138. setTimeKey(Date.now())
  139. }, 50)
  140. }, [])
  141. // 从仓库获取列表
  142. const A2TableList = useSelector((state: RootState) => state.A2Psychz.A2TableList)
  143. // 页码变化
  144. const paginationChange = useCallback(
  145. () => (pageNum: number, pageSize: number) => {
  146. setTableSelect({ ...tableSelect, pageNum, pageSize })
  147. setTimeout(() => {
  148. setTimeKey(Date.now())
  149. }, 50)
  150. },
  151. [tableSelect]
  152. )
  153. // 点击删除
  154. const delByIdFu = useCallback(
  155. async (id: number) => {
  156. const res = await A2_APIremoves(id)
  157. if (res.code === 0) {
  158. MessageFu.success('删除成功!')
  159. getListFu()
  160. getCityFu()
  161. }
  162. },
  163. [getCityFu, getListFu]
  164. )
  165. const columns = useMemo(() => {
  166. return [
  167. {
  168. title: '站址地区',
  169. render: (item: A2tableType) =>
  170. !item.province && !item.city && !item.region
  171. ? '(空)'
  172. : `${item.province}-${item.city}-${item.region}`
  173. },
  174. {
  175. title: '详细地址',
  176. render: (item: A2tableType) =>
  177. item.address ? (
  178. item.address.length >= 25 ? (
  179. <span style={{ cursor: 'pointer' }} title={item.address}>
  180. {item.address.substring(0, 25) + '...'}
  181. </span>
  182. ) : (
  183. item.address
  184. )
  185. ) : (
  186. '(空)'
  187. )
  188. },
  189. {
  190. title: '站址名称',
  191. render: (item: A2tableType) => item.name || '(空)'
  192. },
  193. {
  194. title: '站址编号',
  195. render: (item: A2tableType) => item.siteNum || '(空)'
  196. },
  197. {
  198. title: '机房编码',
  199. dataIndex: 'roomNum'
  200. },
  201. {
  202. title: '项目经理',
  203. render: (item: A2tableType) => {
  204. if (item.pmUserId === 1) return '管理员'
  205. else {
  206. return item.pmName || '(空)'
  207. }
  208. }
  209. },
  210. {
  211. title: '最近编辑时间',
  212. dataIndex: 'updateTime'
  213. },
  214. {
  215. title: '录入方式',
  216. render: (item: A2tableType) =>
  217. item.typeIn === 'pc' ? '系统' : item.typeIn === 'app-scan' ? '移动端扫码' : '移动端手工'
  218. },
  219. {
  220. title: '操作',
  221. render: (item: A2tableType) => (
  222. <>
  223. <Button
  224. size='small'
  225. type='text'
  226. onClick={() => setOpenInfo({ id: item.id, txt: '编辑' })}
  227. >
  228. 编辑
  229. </Button>
  230. <Popconfirm
  231. title='删除后无法恢复,是否删除?'
  232. okText='删除'
  233. cancelText='取消'
  234. onConfirm={() => delByIdFu(item.id)}
  235. okButtonProps={{ loading: false }}
  236. >
  237. <Button size='small' type='text' danger>
  238. 删除
  239. </Button>
  240. </Popconfirm>
  241. </>
  242. )
  243. }
  244. ]
  245. }, [delByIdFu])
  246. const [openInfo, setOpenInfo] = useState<A1addType>({ id: 0, txt: '' })
  247. // 点击导出
  248. const deriveFu = useCallback(async () => {
  249. if (A2TableList.total > 30000)
  250. return MessageFu.warning('只支持导出最多30000条数据。请增加筛选条件,并重新尝试')
  251. if (A2TableList.list.length === 0) return MessageFu.warning('当前搜索条件没有数据!')
  252. const name = '机房管理' + dayjs(new Date()).format('YYYY-MM-DD HH:mm')
  253. const objTemp: any = {}
  254. if (tableSelectRef.current.siteArr) {
  255. const temp = tableSelectRef.current.siteArr
  256. objTemp.province = temp[0] || ''
  257. objTemp.city = temp[1] || ''
  258. objTemp.region = temp[2] || ''
  259. }
  260. const res = await A2_APIgetlistDerive({
  261. ...tableSelectRef.current,
  262. ...objTemp,
  263. pageNum: 1,
  264. pageSize: 99999
  265. })
  266. if (res.code === 0) {
  267. if (res.data.records.length <= 0) return MessageFu.warning('当前搜索条件没有数据!')
  268. const option = {
  269. fileName: name,
  270. datas: [
  271. {
  272. sheetData: res.data.records.map((v: A2tableType) => ({
  273. ...v,
  274. myCity:
  275. !v.province && !v.city && !v.region
  276. ? '(空)'
  277. : `${v.province}-${v.city}-${v.region}`,
  278. address: v.address || '(空)',
  279. name: v.name || '(空)',
  280. siteNum: v.siteNum || '(空)',
  281. pmName: v.pmUserId === 1 ? '管理员' : v.pmName || '(空)',
  282. typeIn:
  283. v.typeIn === 'pc' ? '系统' : v.typeIn === 'app-scan' ? '移动端扫码' : '移动端手工'
  284. })),
  285. sheetName: name,
  286. sheetFilter: [
  287. 'myCity',
  288. 'address',
  289. 'name',
  290. 'siteNum',
  291. 'roomNum',
  292. 'pmName',
  293. 'updateTime',
  294. 'typeIn'
  295. ],
  296. sheetHeader: [
  297. '站址地区',
  298. '详细地址',
  299. '站址名称',
  300. '站址编号',
  301. '机房编码',
  302. '项目经理',
  303. '最近编辑时间',
  304. '录入方式'
  305. ],
  306. columnWidths: [10, 10, 10, 10, 10, 10, 10, 10]
  307. }
  308. ]
  309. }
  310. const toExcel = new ExportJsonExcel(option) //new
  311. toExcel.saveExcel() //保存
  312. }
  313. }, [A2TableList.list.length, A2TableList.total])
  314. return (
  315. <div className={styles.A2Psychz}>
  316. <div className='pageTitle'>
  317. {openInfo.txt === '新增' ? '新增机房' : openInfo.txt === '编辑' ? '编辑机房' : '机房管理'}
  318. </div>
  319. {/* 顶部筛选 */}
  320. <div className='A2top'>
  321. {/* 左侧输入框 */}
  322. <div className='A2top1'>
  323. <div className='A2topRow'>
  324. <span>项目经理:</span>
  325. <Input
  326. key={inputKey}
  327. maxLength={10}
  328. style={{ width: 240 }}
  329. placeholder='请输入姓名,最多10字'
  330. allowClear
  331. onChange={e => txtChangeFu(e.target.value, 'pmUser')}
  332. />
  333. </div>
  334. <div className='A2topRow'>
  335. <span>站址地区:</span>
  336. <Cascader
  337. disabled={!!tableSelect.isSiteEmpty}
  338. changeOnSelect
  339. style={{ width: 308 }}
  340. options={optionsCity}
  341. value={tableSelect.siteArr}
  342. placeholder='全部'
  343. onChange={e => {
  344. setTableSelect({
  345. ...tableSelect,
  346. siteArr: e as string[]
  347. })
  348. }}
  349. />
  350. </div>
  351. <div className='A2topRow'>
  352. <Checkbox
  353. checked={!!tableSelect.isSiteEmpty}
  354. onChange={e =>
  355. setTableSelect({
  356. ...tableSelect,
  357. siteArr: e.target.checked ? undefined : tableSelect.siteArr,
  358. isSiteEmpty: e.target.checked ? 1 : 0
  359. })
  360. }
  361. >
  362. 仅查看站址地区为空的场景
  363. </Checkbox>
  364. </div>
  365. </div>
  366. <div className='A2top2'>
  367. <div>
  368. <div className='A2topRow'>
  369. <span>录入方式:</span>
  370. <Select
  371. style={{ width: 240 }}
  372. value={tableSelect.typeIn}
  373. onChange={e => setTableSelect({ ...tableSelect, typeIn: e })}
  374. options={options1}
  375. />
  376. </div>
  377. <div className='A2topRow'>
  378. <span>搜索项:</span>
  379. <Input
  380. key={inputKey}
  381. maxLength={24}
  382. style={{ width: 322 }}
  383. placeholder='请输入站址名称/站置编号/机房编码,最多24字'
  384. allowClear
  385. onChange={e => txtChangeFu(e.target.value, 'searchKey')}
  386. />
  387. </div>
  388. </div>
  389. {/* 按钮 */}
  390. <div>
  391. <Button onClick={resetSelectFu}>重置</Button>&emsp;
  392. <Button type='primary' onClick={clickSearch}>
  393. 查询
  394. </Button>
  395. &emsp;
  396. <Button type='primary' onClick={() => setOpenInfo({ id: -1, txt: '新增' })}>
  397. 新增
  398. </Button>
  399. &emsp;
  400. <Button type='primary' onClick={deriveFu}>
  401. 导出表格
  402. </Button>
  403. </div>
  404. </div>
  405. </div>
  406. {/* 表格主体 */}
  407. <div className='tableMain'>
  408. <Table
  409. scroll={{ y: 578 }}
  410. dataSource={A2TableList.list}
  411. columns={columns}
  412. rowKey='id'
  413. pagination={{
  414. showQuickJumper: true,
  415. position: ['bottomCenter'],
  416. showSizeChanger: true,
  417. current: tableSelect.pageNum,
  418. pageSize: tableSelect.pageSize,
  419. total: A2TableList.total,
  420. onChange: paginationChange()
  421. }}
  422. />
  423. </div>
  424. {/* 新增和编辑出来的页面 */}
  425. {openInfo.id ? (
  426. <AddPsychz
  427. openInfo={openInfo}
  428. closeFu={() => setOpenInfo({ id: 0, txt: '' })}
  429. upTableFu={() => {
  430. getListFu()
  431. getCityFu()
  432. }}
  433. addTableFu={() => {
  434. resetSelectFu()
  435. getCityFu()
  436. }}
  437. />
  438. ) : null}
  439. {/* 右下角的列表数量 */}
  440. <div className='tableNumBox'>
  441. 共 <span>{A2TableList.total}</span> 条数据
  442. </div>
  443. </div>
  444. )
  445. }
  446. const MemoA2Psychz = React.memo(A2Psychz)
  447. export default MemoA2Psychz