yangpei
4 years ago
12 changed files with 499 additions and 12 deletions
@ -0,0 +1,67 @@ |
|||||
|
import React, { useEffect } from 'react' |
||||
|
import Log from './log' |
||||
|
import logStore, { ConsoleType } from './store' |
||||
|
|
||||
|
const MyConsole: React.FC = () => { |
||||
|
const console: any = {} |
||||
|
|
||||
|
useEffect(() => { |
||||
|
executeConsole() |
||||
|
}, []) |
||||
|
|
||||
|
const processLog = (item: ConsoleType) => { |
||||
|
const { logType } = item |
||||
|
let { infos = [] } = item |
||||
|
if (!infos.length) return |
||||
|
infos = [].slice.call(infos || []) |
||||
|
logStore.addLog({ logType, infos }) |
||||
|
printOriginLog(item) |
||||
|
} |
||||
|
|
||||
|
const clearLog = () => { |
||||
|
logStore.clearLog() |
||||
|
} |
||||
|
|
||||
|
const printOriginLog = (item: ConsoleType) => { |
||||
|
const { logType, infos } = item |
||||
|
if (typeof console[logType] === 'function') { |
||||
|
console[logType].apply(window.console, infos) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const executeConsole = () => { |
||||
|
if (!window.console) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 模拟浏览器中的console方法
|
||||
|
const methodList = ['log', 'info', 'warn', 'debug', 'error'] |
||||
|
methodList.map(method => { |
||||
|
// @ts-ignore
|
||||
|
console[method] = window.console[method] |
||||
|
}) |
||||
|
|
||||
|
methodList.map(method => { |
||||
|
// @ts-ignore
|
||||
|
window.console[method] = (...args: any[]) => { |
||||
|
processLog({ |
||||
|
logType: method, |
||||
|
infos: args |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
window.console.clear = (...args: any[]) => { |
||||
|
clearLog() |
||||
|
console.clear.apply(window.console, args) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div style={{ height: '100%' }}> |
||||
|
<Log logList={logStore.computeLogList} /> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default MyConsole |
@ -0,0 +1,59 @@ |
|||||
|
import React from 'react' |
||||
|
import DataView from '../logView' |
||||
|
import { ConsoleType } from '../store' |
||||
|
|
||||
|
interface Props { |
||||
|
logList: ConsoleType[] |
||||
|
} |
||||
|
|
||||
|
const Log: React.FC<Props> = (props) => { |
||||
|
const renderLogList = (logList: ConsoleType[]) => { |
||||
|
return logList.map((log: ConsoleType, index: number) => { |
||||
|
const dataView = log.infos.map((info: any, i: number) => { |
||||
|
return typeof info !== 'object' || info === null ? |
||||
|
<div key={i}>{`${info}`}</div> : |
||||
|
<DataView data={info} key={i} /> |
||||
|
}) |
||||
|
let ele = null |
||||
|
|
||||
|
// 根据不同console类型展示不同的样式
|
||||
|
switch (log.logType) { |
||||
|
case 'warn': |
||||
|
ele = <div |
||||
|
key={index} |
||||
|
style={{ |
||||
|
background: '#fffacd', |
||||
|
color: 'orange', |
||||
|
border: '1px solid #ffb930' |
||||
|
}} |
||||
|
> |
||||
|
{dataView} |
||||
|
</div> |
||||
|
break |
||||
|
case 'log': |
||||
|
ele = <div key={index}> |
||||
|
{dataView} |
||||
|
</div> |
||||
|
break |
||||
|
case 'error': |
||||
|
ele = <div |
||||
|
key={index} |
||||
|
style={{ |
||||
|
background: '#ffe4e1', |
||||
|
border: '1px solid #f4a0ab' |
||||
|
}} |
||||
|
> |
||||
|
<div style={{ color: '#dc143c' }}>{dataView}</div> |
||||
|
</div> |
||||
|
break |
||||
|
} |
||||
|
return ele |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div style={{ height: '100%', overflow: 'auto' }}>{renderLogList(props.logList)}</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Log |
@ -0,0 +1,114 @@ |
|||||
|
import React, { useState, useEffect } from 'react' |
||||
|
// import DataList from './dataList'
|
||||
|
import Simple from './simple' |
||||
|
import styles from './index.module.less' |
||||
|
|
||||
|
const previewComplex = (data: any) => { |
||||
|
if (Array.isArray(data)) { |
||||
|
return <span>Array[{data.length}]</span> |
||||
|
} |
||||
|
switch (typeof data) { |
||||
|
case 'function': |
||||
|
return <span>{data.name || 'fn'}()</span> |
||||
|
case 'object': |
||||
|
case 'undefined': |
||||
|
case null: |
||||
|
return '{…}' |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
interface Props { |
||||
|
startOpen?: boolean |
||||
|
noSort?: boolean |
||||
|
name: string |
||||
|
value: any |
||||
|
} |
||||
|
|
||||
|
interface State { |
||||
|
open: boolean |
||||
|
loading?: boolean |
||||
|
} |
||||
|
|
||||
|
const DataItem: React.FC<Props> = (props) => { |
||||
|
const [state, setState] = useState<State>({ open: !!props.startOpen, loading: false }) |
||||
|
let opener = null |
||||
|
let children = null |
||||
|
let { name } = props |
||||
|
let preview |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (state.open && props.value) { |
||||
|
setState({ loading: true, open: true }) |
||||
|
} |
||||
|
}, []) |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const data = props.value |
||||
|
const otype = typeof data |
||||
|
let complex = true |
||||
|
|
||||
|
if ( |
||||
|
otype === 'number' || |
||||
|
otype === 'string' || |
||||
|
data == null || |
||||
|
otype === 'boolean' |
||||
|
) { |
||||
|
preview = <Simple data={data} /> |
||||
|
complex = false |
||||
|
} else { |
||||
|
preview = previewComplex(data) |
||||
|
} |
||||
|
|
||||
|
const open = state.open |
||||
|
|
||||
|
if (complex && Object.keys(props.value).length) { |
||||
|
opener = ( |
||||
|
<div onClick={toggleOpen()} className={styles.opener}> |
||||
|
{open ? |
||||
|
<span className={styles.openerOpen} /> : |
||||
|
<span className={styles.openerClose} /> |
||||
|
} |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
// if (complex && open) {
|
||||
|
// children = <DataList data={data} />
|
||||
|
// }
|
||||
|
|
||||
|
if (name.length > 50) { |
||||
|
name = name.slice(0, 50) + '…' |
||||
|
} |
||||
|
}, [props]) |
||||
|
|
||||
|
const toggleOpen = () => { |
||||
|
return () => { |
||||
|
const data = props.value |
||||
|
const isArray = Array.isArray(data) |
||||
|
|
||||
|
if (state.loading) return |
||||
|
if (!isArray && !Object.keys(props.value).length) return |
||||
|
if (isArray && data.length === 0) return |
||||
|
|
||||
|
setState({ |
||||
|
open: !state.open |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<li className={styles.codeWrap}> |
||||
|
<div className={styles.codeBox}> |
||||
|
{opener} |
||||
|
<div onClick={toggleOpen()} className={styles.codeKey}> |
||||
|
{name ? `${name}:` : ''} |
||||
|
</div> |
||||
|
<div className={styles.codeVal}>{preview}</div> |
||||
|
</div> |
||||
|
{children} |
||||
|
</li> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default DataItem |
@ -0,0 +1,64 @@ |
|||||
|
import React, { useState, useEffect } from 'react' |
||||
|
import DataItem from './dataItem' |
||||
|
import styles from './index.module.less' |
||||
|
|
||||
|
interface DataListProps { |
||||
|
data: any |
||||
|
startOpen?: boolean |
||||
|
noSort?: boolean |
||||
|
} |
||||
|
|
||||
|
const alphanumericSort = (a: string, b: string): number => { |
||||
|
if ('' + +a === a) { |
||||
|
if ('' + +b !== b) { |
||||
|
return -1 |
||||
|
} |
||||
|
return +a < +b ? -1 : 1 |
||||
|
} |
||||
|
return a < b ? -1 : 1 |
||||
|
} |
||||
|
|
||||
|
const DataList: React.FC<DataListProps> = (props) => { |
||||
|
const [element, setElement] = useState<any[]>([]) |
||||
|
const renderItem = (name: string, key: string) => { |
||||
|
const data = props.data |
||||
|
return ( |
||||
|
<DataItem |
||||
|
key={key} |
||||
|
name={name} |
||||
|
startOpen={props.startOpen} |
||||
|
value={data[name]} |
||||
|
/> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const { data } = props |
||||
|
if (!data) return |
||||
|
|
||||
|
const isArray = Array.isArray(data) |
||||
|
const ele: any[] = [] |
||||
|
if (isArray) { |
||||
|
data.forEach((item: any, i: number) => { |
||||
|
ele.push(renderItem(String(i), String(i))) |
||||
|
}) |
||||
|
} else { |
||||
|
const names = Object.keys(data) |
||||
|
if (!props.noSort) { |
||||
|
names.sort(alphanumericSort) |
||||
|
} |
||||
|
names.forEach((name, i) => { |
||||
|
ele.push(renderItem(name, name)) |
||||
|
}) |
||||
|
} |
||||
|
setElement(ele) |
||||
|
}) |
||||
|
|
||||
|
return ( |
||||
|
!element.length ? |
||||
|
<div>{Array.isArray(props.data) ? 'Empty array' : 'Empty object'}</div> : |
||||
|
<ul className={styles.codeContainer}>{element}</ul> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default DataList |
@ -0,0 +1,48 @@ |
|||||
|
.codeContainer { |
||||
|
padding: 10px; |
||||
|
font-size: 12px; |
||||
|
list-style: none; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
.codeBox { |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.codeVal { |
||||
|
display: flex; |
||||
|
white-space: pre; |
||||
|
word-break: break-word; |
||||
|
margin-left: 10px; |
||||
|
flex: 1; |
||||
|
color: #183691; |
||||
|
} |
||||
|
|
||||
|
.codeKey { |
||||
|
margin-left: 5px; |
||||
|
color: #905; |
||||
|
} |
||||
|
|
||||
|
.opener { |
||||
|
cursor: pointer; |
||||
|
margin-left: -10px; |
||||
|
position: absolute; |
||||
|
} |
||||
|
|
||||
|
.openerOpen { |
||||
|
border-color: #bbb transparent transparent transparent; |
||||
|
border-style: solid; |
||||
|
border-width: 6px 4px 0 4px; |
||||
|
display: inline-block; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
|
||||
|
.openerClose { |
||||
|
border-color: transparent transparent transparent #bbb; |
||||
|
border-style: solid; |
||||
|
border-width: 4px 0 4px 6px; |
||||
|
display: inline-block; |
||||
|
vertical-align: middle; |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
import React, { useState, useEffect } from 'react' |
||||
|
import DataList from './dataList' |
||||
|
import styles from './index.module.less' |
||||
|
|
||||
|
interface DataViewProps { |
||||
|
data: any |
||||
|
startOpen?: boolean |
||||
|
} |
||||
|
|
||||
|
interface State { |
||||
|
open: boolean |
||||
|
} |
||||
|
|
||||
|
const DataView: React.FC<DataViewProps> = (props) => { |
||||
|
const [state, setState] = useState<State>({ open: !!props.startOpen }) |
||||
|
let children = null |
||||
|
let preview |
||||
|
|
||||
|
const toggleOpen = () => { |
||||
|
return () => { |
||||
|
setState({ |
||||
|
open: !state.open |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const { data } = props |
||||
|
const { open } = state |
||||
|
if (!data) { |
||||
|
return |
||||
|
} |
||||
|
const isArray = Array.isArray(data) |
||||
|
children = open ? <DataList data={data} /> : null |
||||
|
if (isArray) { |
||||
|
preview = '[...]' |
||||
|
} else { |
||||
|
preview = '{...}' |
||||
|
} |
||||
|
}, [props]) |
||||
|
|
||||
|
return ( |
||||
|
<ul className={styles.codeContainer}> |
||||
|
<li className={styles.codeWrap}> |
||||
|
<div className={styles.codeBox}> |
||||
|
<div onClick={toggleOpen()} className={styles.opener}> |
||||
|
{state.open ? ( |
||||
|
<span className={styles.openerOpen} /> |
||||
|
) : ( |
||||
|
<span className={styles.openerClose} /> |
||||
|
)} |
||||
|
</div> |
||||
|
<div className={styles.codeVal}>{preview}</div> |
||||
|
</div> |
||||
|
{children} |
||||
|
</li> |
||||
|
</ul> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default DataView |
@ -0,0 +1,28 @@ |
|||||
|
import React, { useState, useEffect } from 'react' |
||||
|
|
||||
|
interface Props { |
||||
|
data: any |
||||
|
} |
||||
|
|
||||
|
const Simple: React.FC<Props> = (props) => { |
||||
|
const [data, setData] = useState(props.data) |
||||
|
|
||||
|
const valueToText = (value: any) => { |
||||
|
if (value === undefined) { |
||||
|
return 'undefined' |
||||
|
} else if (typeof value === 'number') { |
||||
|
return value.toString() |
||||
|
} |
||||
|
return JSON.stringify(value) |
||||
|
} |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (typeof data === 'string' && data.length > 200) { |
||||
|
setData(data.slice(0, 200) + '…') |
||||
|
} |
||||
|
}, [props]) |
||||
|
|
||||
|
return <div style={{ color: '#333' }}>{valueToText(data)}</div> |
||||
|
} |
||||
|
|
||||
|
export default Simple |
@ -0,0 +1,35 @@ |
|||||
|
export interface ConsoleType { |
||||
|
logType: string |
||||
|
infos: any[] |
||||
|
} |
||||
|
|
||||
|
export class ConsoleStore { |
||||
|
logList: ConsoleType[] = [] |
||||
|
logType: string = 'All' |
||||
|
|
||||
|
addLog(log: ConsoleType) { |
||||
|
this.logList.push(log) |
||||
|
} |
||||
|
|
||||
|
clearLog() { |
||||
|
this.logList = [] |
||||
|
} |
||||
|
|
||||
|
get computeLogList() { |
||||
|
let ret: ConsoleType[] = [] |
||||
|
if (this.logType === 'All') { |
||||
|
ret = this.logList |
||||
|
} else { |
||||
|
ret = this.logList.filter((item) => { |
||||
|
return item.logType === this.logType.toLowerCase() |
||||
|
}) |
||||
|
} |
||||
|
return ret |
||||
|
} |
||||
|
|
||||
|
changeLogType(type: string) { |
||||
|
this.logType = type |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default new ConsoleStore() |
Loading…
Reference in new issue