/* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */ import React from 'react' import { compose, Dispatch } from 'redux' import { connect } from 'react-redux' import { createStructuredSelector } from 'reselect' import memoizeOne from 'memoize-one' import Helmet from 'react-helmet' import { Link } from 'react-router-dom' import { RouteComponentWithParams } from 'utils/types' import injectReducer from 'utils/injectReducer' import injectSaga from 'utils/injectSaga' import reducer from './reducer' import sagas from './sagas' import { checkNameUniqueAction } from 'containers/App/actions' import { ViewActions, ViewActionType } from './actions' import { makeSelectLoading, makeSelectViews } from './selectors' import { makeSelectCurrentProject } from 'containers/Projects/selectors' import ModulePermission from '../Account/components/checkModulePermission' import { initializePermission } from '../Account/components/checkUtilPermission' import { Breadcrumb, Button, Col, Dropdown, Icon, Menu, message, Popconfirm, Row, Spin, Table, Tooltip, Tree } from 'antd' import { ColumnProps, PaginationConfig, SorterResult } from 'antd/lib/table' import { ButtonProps } from 'antd/lib/button' import Container, { ContainerBody, ContainerTitle } from 'components/Container' import Box from 'components/Box' import SearchFilterDropdown from 'components/SearchFilterDropdown' import CopyModal from './components/CopyModal' import CatalogueModal from './components/CatalogueModal' import { ICatalogue, IViewBase, IViewLoading } from './types' import { IProject } from '../Projects/types' import utilStyles from 'assets/less/util.less' import styles from './index.less' import request from 'utils/request' import api from 'utils/api' import classnames from 'classnames' interface IViewListStateProps { views: IViewBase[] currentProject: IProject loading: IViewLoading } interface IViewListDispatchProps { onLoadViews: (projectId: number, parentId: number) => void onDeleteView: (viewId: number, resolve: () => void) => void onCopyView: (view: IViewBase, resolve: () => void) => void onCheckName: (data, resolve, reject) => void } type IViewListProps = IViewListStateProps & IViewListDispatchProps & RouteComponentWithParams // tslint:disable-next-line:interface-name interface Catalogue { description?: string // 资源描述 extConfig?: string // 扩展信息 id?: number industry?: string // 行业分类 name?: string // 资源名称 originDept?: string // 来源部门 originSystem?: string // 来源系统 parentId?: string projectId?: number } interface IViewListStates { screenWidth: number tempFilterViewName: string filterViewName: string filterDropdownVisible: boolean tableSorter: SorterResult copyModalVisible: boolean copyFromView: IViewBase viewList: IViewBase[] catalogueModalVisible: boolean catalogueFromView: ICatalogue saveCatalogueLoading: boolean catalogues: ICatalogue[] selectedCatalogueKeys: string[] treeLoading: boolean tableLoading: boolean } const { TreeNode, DirectoryTree } = Tree export class ViewList extends React.PureComponent { // @ts-ignore public state: Readonly = { screenWidth: document.documentElement.clientWidth, tempFilterViewName: '', filterViewName: '', filterDropdownVisible: false, tableSorter: null, copyModalVisible: false, copyFromView: null, catalogueModalVisible: false, catalogueFromView: null, saveCatalogueLoading: false, catalogues: [], viewList: [], treeLoading: false, tableLoading: false } public async componentWillMount() { const { projectId } = this.props.match.params await (projectId && this.getCatalogues()) // const parentId = Number(this.state.selectedCatalogueKeys[0]) || null // tslint:disable-next-line:no-unused-expression projectId && this.loadViews() window.addEventListener('resize', this.setScreenWidth, false) } private loadViews = async() => { const { projectId } = this.props.match.params if (projectId && this.state.selectedCatalogueKeys.length > 0) { const parentId = Number(this.state.selectedCatalogueKeys[0]) try { this.setState({ tableLoading: true }) const data = await request( api.getViewsByParentId + `?projectId=${projectId}&parentId=${parentId}`, { method: 'get' } ) this.setState({ // @ts-ignore viewList: (data.payload as unknown as IViewBase[]) ?? [] }) } catch (e) { console.log(e) } finally { this.setState({ tableLoading: false }) } } } public componentWillUnmount() { window.removeEventListener('resize', this.setScreenWidth, false) } private setScreenWidth = () => { this.setState({ screenWidth: document.documentElement.clientWidth }) } private getFilterViews = memoizeOne( (viewName: string, views: IViewBase[]) => { if (!Array.isArray(views) || !views.length) { return [] } const regex = new RegExp(viewName, 'gi') return views.filter( (v) => v.name.match(regex) || v.description.match(regex) ) } ) private static getViewPermission = memoizeOne((project: IProject) => ({ viewPermission: initializePermission(project, 'viewPermission'), AdminButton: ModulePermission(project, 'view', true)(Button), EditButton: ModulePermission(project, 'view', false)(Button) })) private getTableColumns = ({ viewPermission, AdminButton, EditButton }: ReturnType) => { // const { views } = this.props const { viewList } = this.state const { tempFilterViewName, filterViewName, filterDropdownVisible, tableSorter } = this.state const sourceNames = viewList.map(({ sourceName }) => sourceName) const columns: Array> = [ { title: '名称', dataIndex: 'name', filterDropdown: ( ), filterDropdownVisible, onFilterDropdownVisibleChange: (visible: boolean) => this.setState({ filterDropdownVisible: visible }), sorter: (a, b) => (a.name > b.name ? 1 : -1), sortOrder: tableSorter && tableSorter.columnKey === 'name' ? tableSorter.order : void 0 }, { title: '描述', dataIndex: 'description' }, { title: '数据源', // title: 'Source', dataIndex: 'sourceName', filterMultiple: false, onFilter: (val, record) => record.sourceName === val, filters: sourceNames .filter((name, idx) => sourceNames.indexOf(name) === idx) .map((name) => ({ text: name, value: name })) } ] if (filterViewName) { const regex = new RegExp(`(${filterViewName})`, 'gi') columns[0].render = (text: string) => ( $1` ) }} /> ) } if (viewPermission) { columns.push({ title: '操作', width: 150, className: utilStyles.textAlignCenter, render: (_, record) => ( ) }) } return columns } private tableChange = (_1, _2, sorter: SorterResult) => { this.setState({ tableSorter: sorter }) } private filterViewNameChange = (e: React.ChangeEvent) => { this.setState({ tempFilterViewName: e.target.value, filterViewName: '' }) } private searchView = (value: string) => { this.setState({ filterViewName: value, filterDropdownVisible: false }) window.event.preventDefault() } private basePagination: PaginationConfig = { defaultPageSize: 20, showSizeChanger: true } private addView = () => { const { history, match } = this.props history.push( `/project/${ match.params.projectId }/view?parentId=${this.state.selectedCatalogueKeys?.join()}` ) } private copyView = (fromView: IViewBase) => () => { this.setState({ copyModalVisible: true, copyFromView: fromView }) } private copy = (view: IViewBase) => { const { onCopyView } = this.props onCopyView(view, () => { this.setState({ copyModalVisible: false }) message.info('数据资产 复制成功') }) } private cancelCopy = () => { this.setState({ copyModalVisible: false }) } private editView = (viewId: number) => () => { const { history, match } = this.props history.push(`/project/${match.params.projectId}/view/${viewId}`) } private deleteView = (viewId: number) => () => { const { onDeleteView } = this.props onDeleteView(viewId, () => { this.loadViews() }) } private checkViewUniqueName = ( viewName: string, resolve: () => void, reject: (err: string) => void ) => { const { currentProject, onCheckName } = this.props onCheckName( { name: viewName, projectId: currentProject.id }, resolve, reject ) } private getCatalogues = async() => { try { const { projectId } = this.props.match.params this.setState({ treeLoading: true }) // @ts-ignore const { payload } = await request( api.getCatalogues + `?projectId=${projectId}`, { method: 'GET' } ) this.setState({ catalogues: payload as unknown as ICatalogue[], selectedCatalogueKeys: payload?.[0]?.id ? [`${payload?.[0]?.id}`] : [] }) } catch (e) { console.log() } finally { this.setState({ treeLoading: false }) } } private handleSaveCatalogue = async(catalogue: ICatalogue) => { const { projectId } = this.props.match.params if (!projectId) { return } try { const parentId = Number(this.state.selectedCatalogueKeys[0]) || null const catalogueFromView = this.state.catalogueFromView this.setState({ saveCatalogueLoading: true }) if (catalogueFromView) { await request(api.updateCatalogue + `/${catalogueFromView.id}`, { method: 'PUT', data: { ...catalogue, projectId } }) } else { await request(api.createCatalogue, { method: 'post', data: { ...catalogue, parentId: catalogue.parentId === '-1' ? null : catalogue.parentId, projectId } }) } this.setState({ catalogueModalVisible: false }) this.getCatalogues() } finally { this.setState({ saveCatalogueLoading: false, catalogueFromView: null }) } } private handleEditCatalogue = (c: ICatalogue) => { this.setState({ catalogueModalVisible: true, catalogueFromView: c }) } private handleDeleteCatalogue = async(c: ICatalogue) => { try { this.setState({ treeLoading: true }) const data = await request(api.deleteCatalogue + `/${c.id}`, { method: 'DELETE' }) // @ts-ignore if (data?.header?.code === 200) { message.success({ content: '删除成功' }) this.getCatalogues() } else { // @ts-ignore // tslint:disable-next-line:no-unused-expression data?.header?.msg && message.error({ content: data?.header?.msg }) } } finally { this.setState({ treeLoading: false }) } } private renderTree = (catalogues: ICatalogue[]) => { const { selectedCatalogueKeys } = this.state // tslint:disable-next-line:jsx-wrap-multiline return ( <> {catalogues.map((c, idx) => (
{ this.setState({ selectedCatalogueKeys: [`${c.id}`] }, () => { this.loadViews() }) }} > {c.name} ( this.handleEditCatalogue(c)} > 编辑 this.handleDeleteCatalogue(c)} > 删除 )} trigger={['click']} >
{c.children && this.renderTree(c.children)}
))} ) } public render() { const { currentProject, views, loading } = this.props const { screenWidth, filterViewName, viewList } = this.state const { viewPermission, AdminButton, EditButton } = ViewList.getViewPermission(currentProject) const tableColumns = this.getTableColumns({ viewPermission, AdminButton, EditButton }) const tablePagination: PaginationConfig = { ...this.basePagination, simple: screenWidth <= 768 } const filterViews = this.getFilterViews(filterViewName, viewList) const { copyModalVisible, copyFromView, catalogueModalVisible, catalogueFromView } = this.state const pathname = this.props.history.location.pathname return ( <> {!pathname.includes('dataManager') && ( View )} 数据资产列表
资源目录列表
{ this.setState({ catalogueModalVisible: true }) }} >
{this.renderTree(this.state.catalogues)}
this.setState({ catalogueModalVisible: false })} /> ) } } const mapDispatchToProps = (dispatch: Dispatch) => ({ onLoadViews: (projectId, parentId) => dispatch(ViewActions.loadViews(projectId, parentId)), onDeleteView: (viewId, resolve) => dispatch(ViewActions.deleteView(viewId, resolve)), onCopyView: (view, resolve) => dispatch(ViewActions.copyView(view, resolve)), // @ts-ignore onCheckName: (data, resolve, reject) => // @ts-ignore dispatch(checkNameUniqueAction('view', data, resolve, reject)) }) const mapStateToProps = createStructuredSelector({ views: makeSelectViews(), currentProject: makeSelectCurrentProject(), loading: makeSelectLoading() }) const withConnect = connect(mapStateToProps, mapDispatchToProps) const withReducer = injectReducer({ key: 'view', reducer }) const withSaga = injectSaga({ key: 'view', saga: sagas }) export default compose(withReducer, withSaga, withConnect)(ViewList)