DatasourceManage.jsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. import React, {
  2. useState,
  3. useEffect,
  4. useImperativeHandle,
  5. forwardRef,
  6. } from 'react'
  7. import {
  8. Space,
  9. Table,
  10. Modal,
  11. message,
  12. Popconfirm,
  13. Button,
  14. Select,
  15. Tabs,
  16. Input,
  17. } from 'antd'
  18. import { SearchOutlined } from '@ant-design/icons'
  19. import DatasourceData from '../component/DatasourceData'
  20. import DatasourceEdit from '../component/DatasourceEdit'
  21. import {
  22. getDataSourceList,
  23. delDataSource,
  24. getTableNamesList,
  25. getDataSourceInfo,
  26. getAilabList,
  27. getAllShareProject,
  28. shareAilab,
  29. getLakeTable,
  30. ailabTablePreview,
  31. ailabSchema,
  32. lakeTablePreview,
  33. lakeSchema,
  34. } from '../services'
  35. const { Option } = Select
  36. const { TabPane } = Tabs
  37. const DatasourceManage = ({ dataType }, ref) => {
  38. // 查看数据源弹窗是否可可视
  39. const [dataModalVisible, setDataModalVisible] = useState(false)
  40. const [dataPreviewVisible, setDataPreviewVisible] = useState(false)
  41. const [dsModalVisible, setDsModalVisible] = useState(false)
  42. // 初始化数据源列表数据 constructor
  43. const [dataSourceList, setDataSourceList] = useState([])
  44. // 初始化ailab列表数据
  45. const [ailabDataList, setAilabDataList] = useState([])
  46. const [filterAilabDataList, setFilterAilabDataList] = useState([])
  47. const [lakeDataList, setLakeDataList] = useState([])
  48. // 表格Loading状态
  49. const [dataLoading, setDataLoading] = useState(false)
  50. // 表名
  51. const [tableNames, setTableNames] = useState([])
  52. // 选中数据源
  53. const [datasourceId, setDatasourceId] = useState(null)
  54. // 查看是否可点击
  55. const [tableWatcher, setTableWatcher] = useState(false)
  56. const [currentDsId, setCurrentDsId] = useState(null)
  57. const [currentDsInfo, setCurrentDsInfo] = useState(null)
  58. const [isShareData, setIsShareData] = useState(false)
  59. //选择表格项
  60. const [selectedRowKeys, setSelectedRowKeys] = useState([])
  61. // 分享的ailab表
  62. const [selectedAilabTable, setSelectedAilabTable] = useState([])
  63. const [projectsList, setProjectsList] = useState([])
  64. const [selectedProjects, setSelectedProjects] = useState([])
  65. // 表内容表头
  66. const [contentCols, setContentCols] = useState([])
  67. // 表内容表数据
  68. const [contentData, setContentData] = useState([])
  69. // 表内容表格Loading状态
  70. const [contentLoading, setContentLoading] = useState(false)
  71. // 表结构表头
  72. const [schemaCols, setScheamCols] = useState([])
  73. // 表结构表数据
  74. const [schemaData, setScheamData] = useState([])
  75. // 表结构表格Loading状态
  76. const [schemaLoading, setSchemaLoading] = useState(false)
  77. const onSelectChange = newSelectedRowKeys => {
  78. setSelectedRowKeys(newSelectedRowKeys)
  79. }
  80. // 获取列表数据
  81. const fetchDataSourceList = async () => {
  82. setDataLoading(true)
  83. const { data } = await getDataSourceList()
  84. if (data.code === 200) {
  85. const list = data.data.items.map(item => {
  86. return {
  87. key: item.id,
  88. datasource_name: item.datasource_name,
  89. datasource: item.datasource,
  90. tag: item.tag,
  91. comments: item.comments,
  92. create_time: item.create_time,
  93. update_time: item.update_time,
  94. }
  95. })
  96. setDataSourceList(list)
  97. } else {
  98. message.error(data.msg)
  99. }
  100. setDataLoading(false)
  101. }
  102. const fetchAilabList = async () => {
  103. setDataLoading(true)
  104. const { data } = await getAilabList()
  105. if (data.code === 200) {
  106. const list = data.data.map(item => {
  107. return {
  108. key: item,
  109. datatable_name: item,
  110. }
  111. })
  112. setAilabDataList(list)
  113. setFilterAilabDataList(list)
  114. } else {
  115. message.error(data.msg)
  116. }
  117. setDataLoading(false)
  118. }
  119. const fetchLakeList = async () => {
  120. setDataLoading(true)
  121. const { data } = await getLakeTable()
  122. if (data.code === 200) {
  123. const list = data.data.map(item => {
  124. return {
  125. key: item.id,
  126. database_name: item.database_name,
  127. datatable_name: item.table_name,
  128. }
  129. })
  130. setLakeDataList(list)
  131. } else {
  132. message.error(data.msg)
  133. }
  134. setDataLoading(false)
  135. }
  136. // 暴露更新列表方法
  137. useImperativeHandle(ref, () => {
  138. return {
  139. updateSourceList: fetchDataSourceList,
  140. updateAilabList: fetchAilabList,
  141. updateLakeList: fetchLakeList,
  142. }
  143. })
  144. // 删除确认
  145. const DeleteConfirm = async key => {
  146. const { data } = await delDataSource(key)
  147. if (data.code === 200) {
  148. message.success('删除成功')
  149. fetchDataSourceList()
  150. } else {
  151. message.success('删除失败')
  152. }
  153. }
  154. //获取数据源表名
  155. const fetchTableNames = async key => {
  156. setTableWatcher(true)
  157. const { data } = await getTableNamesList(key)
  158. if (data.code === 200) {
  159. setDatasourceId(key)
  160. setTableNames(data.data)
  161. setDataModalVisible(true)
  162. } else {
  163. message.error(data.msg)
  164. }
  165. setTableWatcher(false)
  166. }
  167. const handleContent = contentData => {
  168. const colsList = contentData?.header?.map(item => {
  169. return {
  170. title: item,
  171. dataIndex: item,
  172. key: item,
  173. }
  174. })
  175. const dataList = contentData?.content?.map((item, index) => {
  176. const row = {}
  177. row['key'] = index + 1
  178. item.forEach((val, index) => {
  179. const title = contentData.header[index]
  180. row[title] = val
  181. })
  182. return row
  183. })
  184. setContentCols(colsList)
  185. setContentData(dataList)
  186. }
  187. const handleSchema = schemaData => {
  188. const colsList = [
  189. {
  190. title: '序号',
  191. dataIndex: 'order',
  192. key: 'order',
  193. },
  194. {
  195. title: '字段',
  196. dataIndex: 'field',
  197. key: 'field',
  198. },
  199. {
  200. title: '属性',
  201. dataIndex: 'attribute',
  202. key: 'attribute',
  203. },
  204. ]
  205. const dataList = schemaData?.map((item, index) => {
  206. const row = {}
  207. row['key'] = index + 1
  208. const [order, field, attribute] = item.split(':')
  209. row['order'] = order
  210. row['field'] = field
  211. row['attribute'] = attribute
  212. return row
  213. })
  214. setScheamCols(colsList)
  215. setScheamData(dataList)
  216. }
  217. const fetchAilabDataPreview = async (key, type) => {
  218. setContentLoading(true)
  219. const { data } =
  220. type === 'ailab'
  221. ? await ailabTablePreview(key)
  222. : await lakeTablePreview(key)
  223. if (data.code === 200) {
  224. handleContent(data.data)
  225. } else {
  226. message.error(data.msg)
  227. }
  228. setContentLoading(false)
  229. }
  230. const fetchAilabSchema = async (key, type) => {
  231. setSchemaLoading(true)
  232. const { data } =
  233. type === 'ailab' ? await ailabSchema(key) : await lakeSchema(key)
  234. if (data.code === 200) {
  235. handleSchema(data.data)
  236. } else {
  237. message.error(data.msg)
  238. }
  239. setSchemaLoading(false)
  240. }
  241. const watchAilabData = async table_name => {
  242. fetchAilabDataPreview(table_name, 'ailab')
  243. fetchAilabSchema(table_name, 'ailab')
  244. setDataPreviewVisible(true)
  245. }
  246. const watchLakeData = async table_name => {
  247. fetchAilabDataPreview(table_name, 'lake')
  248. fetchAilabSchema(table_name, 'lake')
  249. setDataPreviewVisible(true)
  250. }
  251. useEffect(() => {
  252. if (sessionStorage.getItem('project_id')) {
  253. switch (dataType) {
  254. case 'datasource':
  255. fetchDataSourceList()
  256. break
  257. case 'ailab':
  258. fetchAilabList()
  259. break
  260. case 'datalake':
  261. fetchLakeList()
  262. break
  263. default:
  264. break
  265. }
  266. }
  267. }, [dataType, sessionStorage.getItem('project_id')])
  268. useEffect(() => {
  269. if (isShareData) {
  270. fetchProjects()
  271. }
  272. }, [isShareData])
  273. const fetchProjects = async () => {
  274. const { data } = await getAllShareProject()
  275. if (data.code === 200) {
  276. const list = data.data.map(item => {
  277. return {
  278. label: item.name,
  279. value: item.id,
  280. }
  281. })
  282. setProjectsList(list)
  283. } else {
  284. message.error(data.msg)
  285. }
  286. }
  287. // 格式化事件
  288. const formatTime = time => {
  289. const date = new Date(Number(time) * 1000)
  290. const YY = date.getFullYear()
  291. const MM =
  292. date.getMonth() + 1 < 10
  293. ? '0' + (date.getMonth() + 1)
  294. : date.getMonth() + 1
  295. const DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
  296. const hh = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
  297. const mm =
  298. date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
  299. const ss =
  300. date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
  301. return YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss
  302. }
  303. // 编辑数据
  304. const editData = async id => {
  305. const { data } = await getDataSourceInfo(id)
  306. if (data.code === 200) {
  307. setCurrentDsId(id)
  308. setCurrentDsInfo(data.data)
  309. setDsModalVisible(true)
  310. } else {
  311. message.error(data.msg)
  312. }
  313. }
  314. // 数据源表格项
  315. const ds_columns = [
  316. {
  317. title: '数据源名称',
  318. dataIndex: 'datasource_name',
  319. key: 'datasource_name',
  320. },
  321. {
  322. title: '数据源类型',
  323. dataIndex: 'datasource',
  324. key: 'datasource',
  325. },
  326. {
  327. title: '标签',
  328. dataIndex: 'tag',
  329. key: 'tag',
  330. render: text => <span style={{ color: '#1890ff' }}>{text}</span>,
  331. },
  332. {
  333. title: '详情',
  334. key: 'comments',
  335. dataIndex: 'comments',
  336. },
  337. {
  338. title: '创建时间',
  339. key: 'create_time',
  340. dataIndex: 'create_time',
  341. render: time => formatTime(time),
  342. },
  343. {
  344. title: '更新时间',
  345. key: 'update_time',
  346. dataIndex: 'update_time',
  347. render: time => formatTime(time),
  348. },
  349. {
  350. title: '操作',
  351. key: 'operation',
  352. render: (_, record) => (
  353. <Space size="middle">
  354. <Button
  355. onClick={() => {
  356. fetchTableNames(record.key)
  357. }}
  358. type="link"
  359. disabled={tableWatcher}>
  360. 查看
  361. </Button>
  362. {sessionStorage.getItem('role') === '1' && (
  363. <Button
  364. type="link"
  365. onClick={() => {
  366. editData(record.key)
  367. }}>
  368. 编辑
  369. </Button>
  370. )}
  371. {sessionStorage.getItem('role') === '1' && (
  372. <Popconfirm
  373. title="确认删除?"
  374. okText="确认"
  375. cancelText="取消"
  376. onConfirm={() => DeleteConfirm(record.key)}>
  377. <Button type="link">删除</Button>
  378. </Popconfirm>
  379. )}
  380. </Space>
  381. ),
  382. },
  383. ]
  384. //AIlab表格项
  385. const ailab_columns = [
  386. {
  387. title: '数据表名称',
  388. dataIndex: 'datatable_name',
  389. key: 'datatable_name',
  390. align: 'center',
  391. },
  392. {
  393. title: '操作',
  394. key: 'operation',
  395. align: 'center',
  396. width: '25%',
  397. render: (_, record) => (
  398. <Space size="middle">
  399. {['1', '2', '3'].includes(sessionStorage.getItem('role')) && (
  400. <Button
  401. type="link"
  402. onClick={() => {
  403. setIsShareData(true)
  404. setSelectedAilabTable([record.key])
  405. }}>
  406. 分享
  407. </Button>
  408. )}
  409. <Button
  410. onClick={() => {
  411. watchAilabData(record.key)
  412. }}
  413. type="link">
  414. 查看
  415. </Button>
  416. </Space>
  417. ),
  418. },
  419. ]
  420. const dl_columns = [
  421. {
  422. title: '数据库名称',
  423. dataIndex: 'database_name',
  424. key: 'database_name',
  425. align: 'center',
  426. },
  427. {
  428. title: '数据表名称',
  429. dataIndex: 'datatable_name',
  430. key: 'datatable_name',
  431. align: 'center',
  432. },
  433. {
  434. title: '操作',
  435. key: 'operation',
  436. align: 'center',
  437. width: '25%',
  438. render: (_, record) => (
  439. <Space size="middle">
  440. <Button
  441. onClick={() => {
  442. watchLakeData(record.datatable_name)
  443. }}
  444. type="link">
  445. 查看
  446. </Button>
  447. </Space>
  448. ),
  449. },
  450. ]
  451. // 查看数据源弹窗取消
  452. const handledataModalCancel = () => {
  453. setDataModalVisible(false)
  454. }
  455. const handledsModalCancel = () => {
  456. setDsModalVisible(false)
  457. }
  458. const handledataPreviewCancel = () => {
  459. setDataPreviewVisible(false)
  460. }
  461. // 查看数据源弹窗数据
  462. const dataModal = (
  463. <Modal
  464. title="数据表预览"
  465. visible={dataModalVisible}
  466. onCancel={handledataModalCancel}
  467. footer={null}
  468. destroyOnClose={true}
  469. width={'80%'}
  470. bodyStyle={{ paddingTop: 0 }}>
  471. <DatasourceData data={tableNames} datasourceId={datasourceId} />
  472. </Modal>
  473. )
  474. const dataPreviewModal = (
  475. <Modal
  476. title="数据表预览"
  477. visible={dataPreviewVisible}
  478. onCancel={handledataPreviewCancel}
  479. footer={null}
  480. destroyOnClose={true}
  481. width={'80%'}
  482. bodyStyle={{ paddingTop: 0 }}>
  483. {
  484. <Tabs size={'small'}>
  485. <TabPane
  486. tab="预览表内容"
  487. key="content"
  488. style={{ height: '700px', overflow: 'auto' }}>
  489. <Table
  490. columns={contentCols}
  491. dataSource={contentData}
  492. bordered
  493. loading={contentLoading}
  494. />
  495. </TabPane>
  496. <TabPane
  497. tab="预览表结构"
  498. key="schema"
  499. style={{ height: '700px', overflow: 'auto' }}>
  500. <Table
  501. columns={schemaCols}
  502. dataSource={schemaData}
  503. bordered
  504. loading={schemaLoading}
  505. />
  506. </TabPane>
  507. </Tabs>
  508. }
  509. </Modal>
  510. )
  511. const dsEditModal = (
  512. <Modal
  513. title="编辑数据源"
  514. visible={dsModalVisible}
  515. onCancel={handledsModalCancel}
  516. footer={null}
  517. destroyOnClose={true}
  518. width={'60%'}>
  519. <DatasourceEdit
  520. ds_id={currentDsId}
  521. ds_info={currentDsInfo}
  522. updateDataSource={fetchDataSourceList}
  523. setDsModalVisible={val => setDsModalVisible(val)}
  524. />
  525. </Modal>
  526. )
  527. const rowSelection = {
  528. selectedRowKeys,
  529. onChange: onSelectChange,
  530. columnWidth: '100px',
  531. selections: [
  532. Table.SELECTION_ALL,
  533. Table.SELECTION_INVERT,
  534. Table.SELECTION_NONE,
  535. ],
  536. }
  537. const shareBtn = () => (
  538. <Button
  539. type="link"
  540. disabled={!selectedRowKeys.length}
  541. onClick={() => {
  542. setSelectedAilabTable(selectedRowKeys)
  543. setIsShareData(true)
  544. }}>
  545. 批量分享
  546. </Button>
  547. )
  548. const searchTable = e => {
  549. const list = ailabDataList.filter(item =>
  550. item.datatable_name.includes(e.target.value)
  551. )
  552. setFilterAilabDataList(list)
  553. }
  554. const searchTool = () => (
  555. <Input
  556. type="link"
  557. style={{ width: '210px' }}
  558. onChange={searchTable}
  559. suffix={<SearchOutlined />}
  560. />
  561. )
  562. const shareTable = async () => {
  563. if (selectedProjects.length) {
  564. const parmas = {
  565. table_names: selectedAilabTable,
  566. project_ids: selectedProjects,
  567. }
  568. const { data } = await shareAilab(parmas)
  569. if (data.code === 200) {
  570. message.success('分享成功')
  571. setIsShareData(false)
  572. setSelectedProjects([])
  573. } else {
  574. message.error(data.msg)
  575. }
  576. } else {
  577. message.error('请选择项目')
  578. }
  579. }
  580. const shareMadal = (
  581. <Modal
  582. title="分享数据表"
  583. visible={isShareData}
  584. onOk={shareTable}
  585. okText="确认分享"
  586. onCancel={() => {
  587. setIsShareData(false)
  588. setSelectedProjects([])
  589. }}>
  590. <Space style={{ padding: '10px 20px 30px' }}>
  591. <span>选择分享至:</span>
  592. <Select
  593. mode="multiple"
  594. value={selectedProjects}
  595. onChange={val => setSelectedProjects(val)}
  596. style={{ width: '300px' }}
  597. showArrow
  598. showSearch
  599. optionFilterProp="label"
  600. placeholder="请选择项目">
  601. {projectsList.map(item => {
  602. return (
  603. <Option key={item.value} value={item.value} label={item.label}>
  604. {item.label}
  605. </Option>
  606. )
  607. })}
  608. </Select>
  609. </Space>
  610. </Modal>
  611. )
  612. // 最终渲染
  613. return (
  614. <div>
  615. {dataType === 'datasource' && (
  616. <Table
  617. columns={ds_columns}
  618. dataSource={dataSourceList}
  619. bordered
  620. loading={dataLoading}
  621. />
  622. )}
  623. {dataType === 'ailab' && (
  624. <Table
  625. rowSelection={rowSelection}
  626. columns={ailab_columns}
  627. dataSource={filterAilabDataList}
  628. bordered
  629. loading={dataLoading}
  630. footer={
  631. ['1', '2', '3'].includes(sessionStorage.getItem('role'))
  632. ? shareBtn
  633. : null
  634. }
  635. title={searchTool}
  636. />
  637. )}
  638. {dataType === 'datalake' && (
  639. <Table
  640. columns={dl_columns}
  641. dataSource={lakeDataList}
  642. bordered
  643. loading={dataLoading}
  644. />
  645. )}
  646. {dataModal}
  647. {dataPreviewModal}
  648. {dsEditModal}
  649. {shareMadal}
  650. </div>
  651. )
  652. }
  653. export default forwardRef(DatasourceManage)