index.jsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. import React, { useState, useEffect, useRef, useMemo } from 'react'
  2. import ProTable from '@ant-design/pro-table'
  3. import { useTableScroll } from '@/hooks/useAutoTable'
  4. import { Button, message, Tooltip } from 'antd'
  5. import { connect } from 'dva'
  6. import consts from '@/consts'
  7. import { queryCompany, apiAddCompany } from '@/services/company'
  8. import LazyCascader from '@/components/LazyCascader'
  9. import CompanyDetail from '@/pages/customer/Company/components/CompanyDetail'
  10. import AddCompany from '@/pages/customer/Company/components/AddCompany'
  11. import { useModal } from '@/components/Modal'
  12. import { formatValues } from '@/components/LazyCascader'
  13. import PersonLabel from './components/PersonLabel'
  14. import { TagTypeEnum, TagDataTypeEnum, LabelModeType } from './components/PersonLabel/const'
  15. import { Add, Down, Check } from '@icon-park/react'
  16. import { apiBatchLink } from '@/services/customer'
  17. const Company = props => {
  18. const {
  19. personTagColorMap,
  20. teamTagColorMap,
  21. personTags,
  22. teamTags,
  23. dispatch,
  24. shouldUpdate,
  25. loading
  26. } = props
  27. const { toggleDrawer, setDrawerProps } = useModal()
  28. const tRef = useRef(null)
  29. const [selectKeys, setSelectKeys] = useState([])
  30. const [tagState, setTagState] = useState({
  31. tagIds: [],
  32. tagCooperationIds: []
  33. })
  34. const tag = useMemo(() => {
  35. const personTagMap = {}
  36. const teamTagMap = {}
  37. const personOptions = []
  38. const teamOptions = []
  39. personTags.forEach(item => {
  40. personOptions.push({ label: item.name, value: item.id })
  41. personTagMap[item.id] = {
  42. text: (
  43. <>
  44. <span
  45. className="zh-circle-icon zh-mg-right-3"
  46. style={{ backgroundColor: personTagColorMap[item.id] }}
  47. />
  48. <span>{item.name}</span>
  49. </>
  50. )
  51. }
  52. })
  53. teamTags.forEach(item => {
  54. teamOptions.push({ label: item.name, value: item.id })
  55. teamTagMap[item.id] = {
  56. text: (
  57. <>
  58. <span
  59. className="zh-square-icon zh-mg-right-3"
  60. style={{ backgroundColor: teamTagColorMap[item.id] }}
  61. />
  62. <span>{item.name}</span>
  63. </>
  64. )
  65. }
  66. })
  67. return { personTagMap, teamTagMap, personOptions, teamOptions }
  68. }, [loading])
  69. const [state, setState] = useState({
  70. id: '',
  71. params: {},
  72. visible: false,
  73. orderIds: []
  74. })
  75. const [addCompany, setAddCompany] = useState({
  76. visible: false,
  77. loading: false
  78. })
  79. // 重置到默认值,包括表单
  80. const initData = () => {
  81. tRef.current.reset()
  82. }
  83. // 刷新
  84. const refreshData = () => {
  85. tRef.current.reload()
  86. }
  87. const handleAddCompany = async values => {
  88. setAddCompany({ ...addCompany, loading: true })
  89. const params = formatValues(values)
  90. const { code = -1 } = await apiAddCompany(params)
  91. if (code === consts.RET_CODE.SUCCESS) {
  92. message.success('新增成功')
  93. }
  94. // 请求完了
  95. setAddCompany({ ...addCompany, loading: false, visible: false })
  96. initData()
  97. }
  98. useEffect(() => {
  99. if (!personTags.length) {
  100. dispatch({
  101. type: 'company/fetchTags',
  102. payload: { tagType: TagTypeEnum.PERSONTAG, tagColumn: TagDataTypeEnum.COMPANY }
  103. })
  104. }
  105. if (!teamTags.length) {
  106. dispatch({
  107. type: 'company/fetchTags',
  108. payload: { tagType: TagTypeEnum.TEAMTAG, tagColumn: TagDataTypeEnum.COMPANY }
  109. })
  110. }
  111. if (shouldUpdate) {
  112. tRef.current.reload()
  113. dispatch({
  114. type: 'refresh/company',
  115. payload: 'company'
  116. })
  117. }
  118. }, [shouldUpdate])
  119. const handleSearch = value => {
  120. tRef.current.reset()
  121. setState({ ...state, params: { ...state.params, search: value } })
  122. }
  123. const onDistrictChange = value => {
  124. tRef.current.reset()
  125. const [province = '', city = '', area = ''] = value
  126. setState({ ...state, params: { ...state.params, province, city, area } })
  127. }
  128. // 展示drawer
  129. const showDrawer = id => {
  130. setDrawerProps({
  131. closable: true,
  132. onClose: () => toggleDrawer(),
  133. bodyStyle: {
  134. height: '100vh',
  135. overflowY: 'hidden'
  136. },
  137. children: <CompanyDetail dataId={id} orderIds={state.orderIds} />
  138. })
  139. toggleDrawer()
  140. }
  141. const columns = [
  142. {
  143. title: '客户名称',
  144. dataIndex: 'companyName',
  145. key: 'companyName',
  146. width: 150,
  147. search: false,
  148. sorter: (a, b) => a.companyName.localeCompare(b.companyName),
  149. render: (companyName, record) => (
  150. <span
  151. onClick={() => showDrawer(record.id)}
  152. className="text-primary cursor-pointer hover:text-[#967bbd]">
  153. {companyName}
  154. </span>
  155. )
  156. },
  157. {
  158. title: '地区',
  159. dataIndex: 'local',
  160. key: 'local',
  161. search: false,
  162. width: 100
  163. },
  164. {
  165. title: '个人标签',
  166. dataIndex: 'tagIds',
  167. width: 100,
  168. filters: true,
  169. valueEnum: tag.personTagMap,
  170. valueType: 'select',
  171. fieldProps: {
  172. mode: 'multiple',
  173. options: tag.personOptions,
  174. showArrow: true
  175. },
  176. render: (_, record) => (
  177. <div className="zh-align-center zh-flex-wrap">
  178. {record.tagIds.map(item => (
  179. <Tooltip title={personTags.find(i => i.id === item)?.name} key={record.id + item}>
  180. <span
  181. className="w-12px h-12px inline-block rounded-1/2 mr-1"
  182. style={{ backgroundColor: personTagColorMap[item] }}
  183. />
  184. </Tooltip>
  185. ))}
  186. <PersonLabel
  187. dataId={record.id}
  188. tagIds={record.tagIds}
  189. tagType={TagTypeEnum.PERSONTAG}
  190. tagColumn={TagDataTypeEnum.COMPANY}
  191. checkCallBack={refreshData}
  192. modeType={LabelModeType.column}>
  193. <Add theme="filled" size="20" fill="#868e96" className="cursor-pointer" />
  194. </PersonLabel>
  195. </div>
  196. )
  197. },
  198. {
  199. title: '协作标签',
  200. dataIndex: 'tagCooperationIds',
  201. key: 'tagCooperationIds',
  202. width: 80,
  203. filters: true,
  204. valueEnum: tag.teamTagMap,
  205. valueType: 'select',
  206. fieldProps: {
  207. mode: 'multiple',
  208. options: tag.teamOptions,
  209. showArrow: true
  210. },
  211. render: (_, record) => (
  212. <div className="flex items-center flex-wrap">
  213. {record.tagCooperationIds.map(item => (
  214. <Tooltip title={teamTags.find(i => i.id === item)?.name} key={record.id + item}>
  215. <span
  216. key={record.id + item}
  217. className="w-12px h-12px inline-block mr-1"
  218. style={{ backgroundColor: teamTagColorMap[item] }}
  219. />
  220. </Tooltip>
  221. ))}
  222. <PersonLabel
  223. dataId={record.id}
  224. tagIds={record.tagCooperationIds}
  225. tagType={TagTypeEnum.TEAMTAG}
  226. tagColumn={TagDataTypeEnum.COMPANY}
  227. checkCallBack={refreshData}
  228. modeType={LabelModeType.column}>
  229. <Add theme="filled" size="20" fill="#868e96" className="cursor-pointer" />
  230. </PersonLabel>
  231. </div>
  232. )
  233. },
  234. {
  235. title: '联系人',
  236. dataIndex: 'clientTotal',
  237. key: 'clientTotal',
  238. search: false,
  239. width: 80
  240. },
  241. {
  242. title: '公司性质',
  243. dataIndex: 'nature',
  244. key: 'nature',
  245. search: false,
  246. width: 80
  247. },
  248. {
  249. title: '公司地址',
  250. dataIndex: 'address',
  251. key: 'address',
  252. search: false,
  253. width: 80
  254. },
  255. {
  256. title: '公司传真',
  257. dataIndex: 'fax',
  258. key: 'fax',
  259. search: false,
  260. width: 80
  261. },
  262. {
  263. title: '网址',
  264. dataIndex: 'webservice',
  265. key: 'webservice',
  266. search: false,
  267. width: 80
  268. },
  269. {
  270. title: '乘车路线',
  271. dataIndex: 'ride',
  272. key: 'ride',
  273. search: false,
  274. width: 80
  275. },
  276. {
  277. title: '地标建筑',
  278. dataIndex: 'landmarks',
  279. key: 'landmarks',
  280. search: false,
  281. width: 80
  282. },
  283. {
  284. title: '参考住宿',
  285. dataIndex: 'stay',
  286. key: 'stay',
  287. search: false,
  288. width: 80
  289. },
  290. {
  291. title: '备注',
  292. dataIndex: 'remarks',
  293. key: 'remarks',
  294. search: false,
  295. width: 80
  296. }
  297. ]
  298. const [columnsStateMap, setColumnsStateMap] = useState({
  299. address: {
  300. show: false
  301. },
  302. nature: {
  303. show: false
  304. },
  305. fax: {
  306. show: false
  307. },
  308. webservice: {
  309. show: false
  310. },
  311. ride: {
  312. show: false
  313. },
  314. stay: {
  315. show: false
  316. },
  317. landmarks: {
  318. show: false
  319. },
  320. remarks: {
  321. show: false
  322. }
  323. })
  324. const hanleRowSelectChange = selectedRowKeys => {
  325. setSelectKeys(selectedRowKeys)
  326. }
  327. const toolbarOptionsChange = (type, ids) => {
  328. setTagState({ ...tagState, [type]: ids })
  329. }
  330. const { getScrollRef, redoHeight } = useTableScroll(columns, selectKeys)
  331. // 批量设置-确认触发
  332. const tagsSettingConfirm = async clientIds => {
  333. if (tagState.tagIds.length) {
  334. await apiBatchLink({
  335. tagIds: tagState.tagIds,
  336. dataIds: clientIds,
  337. dataType: TagDataTypeEnum.COMPANY,
  338. tagType: TagTypeEnum.PERSONTAG
  339. })
  340. }
  341. if (tagState.tagCooperationIds.length) {
  342. await apiBatchLink({
  343. tagIds: tagState.tagCooperationIds,
  344. dataIds: clientIds,
  345. dataType: TagDataTypeEnum.COMPANY,
  346. tagType: TagTypeEnum.TEAMTAG
  347. })
  348. }
  349. refreshData()
  350. }
  351. const renderTableAlert = ({ selectedRowKeys }) => {
  352. return (
  353. <div className="flex flex-row">
  354. <div className="mr-5px">
  355. <PersonLabel
  356. tagType={TagTypeEnum.PERSONTAG}
  357. tagColumn={TagDataTypeEnum.COMPANY}
  358. checkCallBack={toolbarOptionsChange}
  359. modeType={LabelModeType.toolbar}>
  360. {tagState.tagIds.length ? (
  361. <Button>
  362. {tagState.tagIds.map(item => (
  363. <span
  364. key={item}
  365. className="w-12px h-12px inline-block rounded-1/2 mr-3px"
  366. style={{ backgroundColor: personTagColorMap[item] }}
  367. />
  368. ))}
  369. <Down />
  370. </Button>
  371. ) : (
  372. <Button>
  373. 个人标签
  374. <Down />
  375. </Button>
  376. )}
  377. </PersonLabel>
  378. </div>
  379. <div className="mr-5px">
  380. <PersonLabel
  381. tagType={TagTypeEnum.TEAMTAG}
  382. tagColumn={TagDataTypeEnum.COMPANY}
  383. checkCallBack={toolbarOptionsChange}
  384. modeType={LabelModeType.toolbar}>
  385. {tagState.tagCooperationIds.length ? (
  386. <Button>
  387. {tagState.tagCooperationIds.map(item => (
  388. <span
  389. key={item}
  390. className="w-12px h-12px inline-block mr-1"
  391. style={{ backgroundColor: teamTagColorMap[item] }}
  392. />
  393. ))}
  394. <Down />
  395. </Button>
  396. ) : (
  397. <Button>
  398. 协作标签
  399. <Down />
  400. </Button>
  401. )}
  402. </PersonLabel>
  403. </div>
  404. <Button onClick={() => tagsSettingConfirm(selectedRowKeys)}>
  405. <Check size={14} />
  406. 确认
  407. </Button>
  408. </div>
  409. )
  410. }
  411. return (
  412. <ProTable
  413. rowKey={record => record.id}
  414. actionRef={tRef}
  415. params={state.params}
  416. request={async (params, sort, filter) => {
  417. const { code = -1, data: { customer = [], total = 0 } = { customer: [], total: 0 } } =
  418. await queryCompany({ ...params, ...sort, ...filter })
  419. setState({ ...state, orderIds: customer.map(item => item.id) })
  420. return {
  421. data: customer,
  422. success: code === consts.RET_CODE.SUCCESS,
  423. total
  424. }
  425. }}
  426. headerTitle="客户"
  427. bordered
  428. search={{ filterType: 'light' }}
  429. toolbar={{
  430. search: {
  431. allowClear: true,
  432. onSearch: handleSearch,
  433. placeholder: '输入客户名称、公司性质等'
  434. },
  435. actions: [
  436. <LazyCascader
  437. onChange={onDistrictChange}
  438. changeOnSelect={true}
  439. key="lazyCascader"
  440. className="hover:bg-[rgba(0,0,0,0.1)]"
  441. bordered={false}
  442. />,
  443. <AddCompany onConfirm={handleAddCompany} key="addCompanyBtn" />
  444. ]
  445. }}
  446. rowSelection={{
  447. onChange: hanleRowSelectChange,
  448. type: 'checkbox',
  449. columnWidth: 25
  450. }}
  451. onLoadingChange={loadingStatus => {
  452. loadingStatus && redoHeight()
  453. }}
  454. tableAlertRender={renderTableAlert}
  455. scroll={getScrollRef}
  456. columnsStateMap={columnsStateMap}
  457. onColumnsStateChange={map => setColumnsStateMap(map)}
  458. columns={columns}
  459. />
  460. )
  461. }
  462. export default connect(({ company, refresh, loading }) => ({
  463. personTagColorMap: company.personTagColorMap,
  464. personTags: company.personTags,
  465. teamTagColorMap: company.teamTagColorMap,
  466. teamTags: company.teamTags,
  467. loading: loading.models.company,
  468. shouldUpdate: refresh.company
  469. }))(Company)