import React , { useState , useEffect , useCallback } from 'react' ; import { useSelector } from '@/store' ; import { Form , Dropdown , Menu , Button , DatePicker , Divider } from 'antd' ; import { UserOutlined , DownOutlined , Loading3QuartersOutlined , DownCircleOutlined , } from '@ant-design/icons' ; import { timeFormatter } from '@/lin/utils/time-formatter' ; import { searchLogKeyword } from '@/lin/utils/search' ; import StickyTop from '@/components/base/sticky-top/StickyTop' ; import LinHeader from '@/components/base/lin-header/LinHeader' ; import LoadingWrapper from '@/components/base/loading-wrapper/LoadingWrapper' ; import LinSearch from '@/components/base/lin-search/LinSearch' ; import LogModel from '@/lin/models/log' ; import useDeepCompareEffect from '@/hooks/base/useDeepCompareEffect' ; import useFirstMountState from '@/hooks/base/useFirstMountState' ; import { checkPermission } from '@/lin/directives/authorize' ; import { ILogUsers , ILogItem , ILogsInfo } from '@/types/model' ; import { Moment } from '@/types/project' ; import './log.scss' ; const SEARCH_DATA_PER_TIME = 15 ; export default function Log ( ) { const [ loading , setLoading ] = useState ( true ) ; const [ moreLoading , setMoreLoading ] = useState ( false ) ; const [ isSearch , setIsSearch ] = useState ( false ) ; const [ users , setUsers ] = useState < ILogUsers > ( { } as ILogUsers ) ; const [ logs , setLogs ] = useState < ILogItem [ ] > ( [ ] ) ; const [ keyword , setKeyword ] = useState ( '' ) ; const [ totalCount , setTotalCount ] = useState ( 0 ) ; const [ finished , setFinished ] = useState ( false ) ; const [ searchUser , setSearchUser ] = useState ( '全部人员' ) ; const [ searchKeyword , setSearchKeyword ] = useState ( '' ) ; const [ searchDate , setSearchDate ] = useState < [ string , string ] | [ ] > ( [ ] ) ; const [ form ] = Form . useForm ( ) ; const user = useSelector ( state => state . app . user ) ; const permissions = useSelector ( state => state . app . permissions ) ; const isFirstMount = useFirstMountState ( ) ; const getSearchData = useCallback ( async ( ) => { const name = searchUser === '全部人员' ? '' : searchUser ; const res = await LogModel . searchLogs ( { page : 0 , keyword : searchKeyword , name , start : searchDate [ 0 ] , end : searchDate [ 1 ] } ) ; if ( typeof res === 'undefined' ) return ; if ( res . items . length > 0 ) { let logs = res . items ; if ( searchKeyword ) { logs = await searchLogKeyword ( searchKeyword , logs ) ; } setLogs ( logs ) ; setTotalCount ( res . total ) ; setFinished ( logs . length < SEARCH_DATA_PER_TIME ) ; } else { setFinished ( true ) ; } setLoading ( false ) ; } , [ searchKeyword , searchUser , searchDate ] ) ; useDeepCompareEffect ( ( ) => { async function initPage ( ) { try { if ( logs . length > 0 ) return ; if ( user . admin || permissions . includes ( '查询日志记录的用户' ) ) { const users = await LogModel . getLoggedUsers ( { } ) ; const res = await LogModel . getLogs ( { page : 0 } ) ; if ( typeof res === 'undefined' ) return ; setUsers ( users ) ; setLogs ( res . items ) ; setTotalCount ( res . total ) ; setFinished ( res . items . length < SEARCH_DATA_PER_TIME ) ; } } catch ( err ) { console . error ( err ) ; } finally { setLoading ( false ) ; } } async function getNextPage ( ) { let res ; if ( isSearch ) { res = await LogModel . moreSearchPage ( ) ; } else { res = await LogModel . moreLogPage ( ) ; } if ( typeof res === 'undefined' ) return ; setMoreLoading ( false ) ; if ( res . items . length > 0 ) { let moreLogs = res . items ; if ( isSearch && searchKeyword ) { moreLogs = await searchLogKeyword ( searchKeyword , moreLogs ) ; } setLogs ( logs . concat ( moreLogs ) ) ; setFinished ( logs . length < SEARCH_DATA_PER_TIME ) ; } else { setFinished ( true ) ; } } loading && ! isSearch && initPage ( ) ; loading && isSearch && getSearchData ( ) ; moreLoading && getNextPage ( ) ; } , [ loading , isSearch , moreLoading , getSearchData , logs , user . admin , permissions , ] ) ; useEffect ( ( ) => { if ( isFirstMount ) return ; function searchPage ( ) { const kwArr = [ ] ; let isSearch = false ; if ( searchUser !== '全部人员' ) { kwArr . push ( searchUser ) ; isSearch = true ; } if ( searchKeyword ) { kwArr . push ( searchKeyword ) ; isSearch = true ; } if ( searchDate . length !== 0 ) { kwArr . push ( `${ searchDate [ 0 ] }至${ searchDate [ 1 ] }` ) ; isSearch = true ; } const keyword = kwArr . join ( ' ' ) ; setKeyword ( keyword ) ; setTotalCount ( 0 ) ; setLogs ( [ ] ) ; setLoading ( true ) ; setIsSearch ( isSearch ) ; setFinished ( false ) ; } searchPage ( ) ; } , [ searchUser , searchKeyword , searchDate , isFirstMount ] ) ; function nextPage ( ) { setMoreLoading ( true ) ; } function backInit ( ) { form . resetFields ( ) ; setIsSearch ( false ) ; setLogs ( [ ] ) ; setKeyword ( '' ) ; setTotalCount ( 0 ) ; setFinished ( false ) ; setSearchUser ( '全部人员' ) ; setSearchKeyword ( '' ) ; setSearchDate ( [ ] ) ; setLoading ( true ) ; } function onSearchChange ( value ) { setSearchKeyword ( value . trim ( ) ) ; } function onDropdownChange ( value ) { setSearchUser ( value ) ; } function onPickerChange ( value , valueStr ) { let begin , end , range = [ ] ; if ( valueStr [ 0 ] !== '' ) { begin = valueStr [ 0 ] + ' 00:00:00' ; end = valueStr [ 1 ] + ' 23:59:59' ; range = [ begin , end ] ; } setSearchDate ( range ) ; } const menu = ( < Menu > < Menu . Item style = { { paddingLeft : '17px' } } onClick = { ( ) => onDropdownChange ( '全部人员' ) } > 全部人员 < /Menu.Item> { ( users . items || [ ] ) . map ( item => ( < Menu . Item key = { item } style = { { paddingLeft : '17px' } } onClick = { ( ) => onDropdownChange ( item ) } > < UserOutlined style = { { marginRight : '6px' } } /> { item } < /Menu.Item>))} < /Menu>); const logsLength = logs && logs . length ; return ( < div className = 'log-container' > { } < StickyTop height = { 60 } > < LinHeader title = '日志信息' divider = { false } style = { { padding : '0 20px' } } > < div className = 'header-right' r - if = { checkPermission ( { permission : '搜索日志' } ) } > < Form form = { form } component = { false } > < Form . Item name = 'search' noStyle > < LinSearch onValueChange = { onSearchChange } /> < /Form.Item> < Form . Item name = 'user' noStyle > < Dropdown className = 'dropdown' placement = 'bottomCenter' overlay = { menu } disabled = { ! checkPermission ( { permission : '查询日志记录的用户' } ) } > < Button > { searchUser } < DownOutlined style = { { marginLeft : '10px' } } /> < /Button> < /Dropdown> < /Form.Item> < Form . Item name = 'date' noStyle > < DatePicker . RangePicker className = 'date-picker' dropdownClassName = 'picker' separator = '至' format = 'YYYY-MM-DD' renderExtraFooter = { undefined } onChange = { onPickerChange } / > < /Form.Item> < /Form> < /div> < /LinHeader> < /StickyTop> { } < div className = 'search' r - if = { isSearch } > < p className = 'search-tip' > 搜索 “ < span className = 'search-keyword' > { keyword } < /span>”， 找到{' '} < span className = 'search-num' > { totalCount } < /span> 条日志信息 < /p> < button className = 'search-back' onClick = { backInit } > 返回全部日志 < /button> < /div> { } < div className = 'content' > < LoadingWrapper loading = { loading } minHeight = { 300 } > < article > { logs . map ( ( item , index ) => ( < section key = { item . time + index } > < span className = 'point-time' > < /span> < aside > < p className = 'things' dangerouslySetInnerHTML = { { __html : item . message } } / > < p className = 'brief' > < span className = 'text-yellow' > { item . username } < /span> { timeFormatter ( item . time ) } < /p> < /aside> < /section>))} < /article> < /LoadingWrapper> < div r - if = { ! loading } > < Divider style = { { height : '2px' } } r - if = { logsLength } / > < div className = 'more' r - if = { logsLength } > < Loading3QuartersOutlined className = 'more-loading-icon' r - if = { moreLoading } / > < div onClick = { nextPage } r - if = { ! loading && ! moreLoading && ! finished } > < span > 查看更多 < /span> < DownCircleOutlined className = 'down-circle-icon' / > < /div> < div r - if = { finished } > < span > { totalCount === 0 ? '暂无数据' : '没有更多数据了' } < /span> < /div> < /div> < div className = 'nothing' r - else > 暂无日志信息 < /div> < /div> < /div> < /div>); }