DatasourceManage.jsx 16 KB

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