antd#Progress TypeScript Examples
The following examples show how to use
antd#Progress.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PasswordStrength.tsx From posthog-foss with MIT License | 6 votes |
export default function PasswordStrength({ password = '' }: { password: string }): JSX.Element {
// passwordScore is 0 if no password input
// passwordScore is 20, 40, 60, 80, or 100 if password input, based on zxcvbn score (which is 0, 1, 2, 3, or 4)
const passwordScore: number = password.length && zxcvbn(password).score * 20 + 20
return (
<Progress
percent={passwordScore}
size="small"
strokeColor={
passwordScore <= 20
? red.primary
: passwordScore <= 40
? volcano.primary
: passwordScore <= 60
? orange.primary
: passwordScore <= 80
? yellow.primary
: green.primary
}
showInfo={false}
/>
)
}
Example #2
Source File: ModalConfigs.tsx From condo with MIT License | 6 votes |
getUploadProgressModalConfig = (
title: string,
processingMessage: string,
okText: string,
onButtonClick: () => void
) => {
return {
title: title,
closable: false,
content: (
<ModalContext.Consumer>
{
({ progress }) => {
return (
<>
<Progress
format={(percent) => Math.floor(percent) + '%'}
percent={progress}
status={'active'}
/>
<Alert
style={{ marginTop: 16 }}
message={processingMessage}
type='info'
/>
</>
)
}
}
</ModalContext.Consumer>
),
okText: okText,
okButtonProps: {
type: 'primary',
danger: true,
},
onOk: onButtonClick,
}
}
Example #3
Source File: useIpcRendererHandle.tsx From Aragorn with MIT License | 6 votes |
useFileDownloadHandle = () => {
useEffect(() => {
ipcRenderer.on('file-download-reply', (_, errMessage?: string) => {
if (errMessage) {
message.error(errMessage);
} else {
message.success('下载成功');
}
});
ipcRenderer.on('file-download-progress', (_, res: { name: string; progress: number; key: string }) => {
const percent = Math.floor(res.progress * 100);
const isFinish = percent === 100;
notification.open({
type: isFinish ? 'success' : 'info',
message: isFinish ? '下载完成' : `正在下载${res.name}`,
description: <Progress percent={percent} />,
key: res.key,
duration: isFinish ? 1.5 : null
});
});
}, []);
}
Example #4
Source File: H5UploadButton.tsx From whiteboard-demo with MIT License | 6 votes |
private renderProgress = () => {
const { t } = this.props;
return (
<div style={{ display: "flex", justifyContent: "center" }}>
<Spin tip={t('upload-finished-loading')} spinning={this.state.deploying} size="large">
<Progress
type="circle"
strokeColor={{
'0%': '#108ee9',
'100%': '#87d068',
}}
percent={this.state.percent}>
</Progress>
</Spin>
</div>
)
}
Example #5
Source File: index.tsx From metaplex with Apache License 2.0 | 6 votes |
WaitingStep = (props: {
createAuction: () => Promise<void>;
confirm: () => void;
}) => {
const [progress, setProgress] = useState<number>(0);
useEffect(() => {
const func = async () => {
const inte = setInterval(
() => setProgress(prog => Math.min(prog + 1, 99)),
600,
);
await props.createAuction();
clearInterval(inte);
props.confirm();
};
func();
}, []);
return (
<div
style={{
marginTop: 70,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Progress type="circle" percent={progress} />
<div className="waiting-title">
Your creation is being listed with Metaplex...
</div>
<div className="waiting-subtitle">This can take up to 30 seconds.</div>
</div>
);
}
Example #6
Source File: GaugeChart.tsx From nebula-dashboard with Apache License 2.0 | 6 votes |
render() {
const { percent } = this.props;
const color = getWhichColor(percent);
const _percent =
percent < 1 ? Number(percent.toFixed(2)) : Math.round(percent);
return (
<Progress
className="nebula-chart nebula-chart-gauge"
strokeLinecap="square"
strokeColor={color}
type="dashboard"
percent={_percent}
width={140}
strokeWidth={20}
/>
);
}
Example #7
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
// 放在更新人前面
planDetailColumns.splice(
4,
0,
{
title: i18n.t('dop:Interface pass rate'),
dataIndex: 'apiPassedCount',
key: 'apiPass',
width: 140,
render: (_text: any, record: TEST_CASE.CaseTableRecord) => {
const { total, passed } = record.apiCount || {};
const percent = record.apiCount ? ((passed || 0) * 100) / total : 0;
return (
record.id && (
<div className="mr-6">
<Progress percent={Math.round(percent)} format={() => `${passed || 0}/${total || 0}`} />
</div>
)
);
},
},
{
title: i18n.t('dop:Executor'),
dataIndex: 'executorID',
key: 'executorID',
width: 100,
render: (text) => <UserInfo id={text} render={(data) => data.nick || data.name} />,
},
);
Example #8
Source File: time-trace.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
TimeTraceBar = React.forwardRef(
({ logged, spent, remain, estimate, active = false, onClick }: IZTraceBarProps, ref) => {
const _logged = numberOr(logged, 0);
const _spent = numberOr(spent, 0);
const _remain = numberOr(remain, 0);
const _estimate = numberOr(estimate, 0);
const [blue, yellow] = calculatePercent(_logged, _spent, _remain, _estimate);
return (
<div className={`time-trace ${active ? 'active-hover' : ''}`} onClick={onClick} ref={ref}>
<Progress strokeColor="#f47201" showInfo={false} success={{ percent: blue }} percent={blue + yellow} />
</div>
);
},
)
Example #9
Source File: summary-detail.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
renderProgress = (data: IData, i: number, shiftingLeft?: any) => (
<div key={i} className="progress-row">
<div className="progress-left">
<span>{data.name}</span>
<span>{getFormatter('TIME').format(data.value, 2)}</span>
</div>
<div className="progress-right">
<div style={shiftingLeft ? { left: shiftingLeft[i], position: 'absolute', width: '100%' } : {}}>
<Progress strokeLinecap="square" percent={data.percent} showInfo={false} />
</div>
</div>
</div>
)
Example #10
Source File: circular.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
Circular: React.FC<IProps> = ({ width = 100, stroke, percent = 0, strokeWidth = 5, className, children }) => {
const [bgColor, frontColor] = stroke;
const wrapperWidth = width + strokeWidth;
return (
<div className="relative" style={{ width: wrapperWidth, height: wrapperWidth }}>
<Progress
width={width + strokeWidth}
strokeWidth={strokeWidth}
strokeColor={bgColor}
percent={100}
success={{ percent, strokeColor: frontColor }}
type="circle"
format={() => ''}
/>
{children ? (
<div className="absolute top-0 left-0" style={{ width: wrapperWidth, height: wrapperWidth }}>
{children}
</div>
) : null}
</div>
);
}
Example #11
Source File: test-list.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
CodeCoverage = ({ data }: { data: Array<{ value: number[] }> }) => {
let rowsTotal = 0;
let rowsCover = 0;
data.forEach((item) => {
rowsTotal += item.value[0] || 0;
rowsCover += item.value[9] || 0;
});
const percent = rowsTotal ? +((rowsCover * 100) / rowsTotal).toFixed(2) : 0;
const title = (
<>
<div>
{i18n.t('dop:Total number of rows')}: {rowsTotal}
</div>
<div>
{i18n.t('dop:Covering the number of rows')}: {rowsCover}
</div>
<div>
{i18n.t('dop:Line coverage')}: {percent}%
</div>
</>
);
return (
<Tooltip title={title} placement="right">
<Progress
percent={100}
success={{
percent: percent,
}}
strokeColor={themeColor['default-02']}
format={(_percent: number, successPercent: number) => `${Math.floor(successPercent)}%`}
/>
</Tooltip>
);
}
Example #12
Source File: test-list.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
ExecuteResult = ({ totals }: { totals: { tests: number; statuses: TEST.Statuses } }) => {
if (!totals) {
return null;
}
const { failed, error, passed, skipped } = totals.statuses;
const { tests } = totals;
const passedPercent = tests ? floor(((passed + skipped) * 100) / tests, 2) : 0;
const title = (
<div>
<div>
{i18n.t('failed')}: {failed}
</div>
<div>
{i18n.t('Error')}: {error}
</div>
<div>
{i18n.t('dop:pass')}: {passed}
</div>
<div>
{i18n.t('dop:Skipped')}: {skipped}
</div>
</div>
);
return (
<Tooltip title={title} placement="right">
<Progress
percent={100}
success={{
percent: passedPercent,
}}
strokeColor={themeColor['default-02']}
format={(_percent: number, successPercent: number) => `${Math.floor(successPercent)}%`}
/>
</Tooltip>
);
}
Example #13
Source File: index.tsx From RareCamp with Apache License 2.0 | 6 votes |
export default function GlobalLoadingIndicator() {
const isFetching = useIsFetching()
const isMutating = useIsMutating()
return isFetching || isMutating ? (
<Progress
style={{ position: 'fixed', top: -12, left: 0, zIndex: 99999 }}
strokeColor={{
from: '#66bb6a',
to: '#5c6bc0',
}}
percent={100}
showInfo={false}
status="active"
/>
) : null
}
Example #14
Source File: utils.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
getRender = (val: Obj, record: Obj, extra?: Extra) => {
const { type, data, key } = val || {};
let Comp: React.ReactNode = null;
const { userMap = {} } = extra || {};
if (!data) {
return '-';
}
switch (type) {
case 'multiple':
{
const { props } = extra || ({} as Extra);
const render = get(props, `columnsRender.${key}`) || defaultMultiple;
const CompMap = {};
map(data, (v, k) => {
CompMap[k] = getRender(v, record, extra);
});
Comp = <div>{render(val, record, CompMap)}</div>;
}
break;
case 'icon':
{
const { type: iconType, url } = data;
if (url) {
Comp = <img src={url} />;
} else if (iconType) {
Comp = <ErdaIcon isConfigPageIcon size={16} type={iconType} />;
} else {
Comp = '';
}
}
break;
case 'duration':
{
const { value, tip } = data;
if (!value) {
Comp = '0s';
} else if (value === -1) {
Comp = '-';
} else {
const _duration = moment.duration(value, 'seconds');
const duration = [
{ value: _duration.years(), unit: 'y' },
{ value: _duration.months(), unit: 'm' },
{ value: _duration.days(), unit: 'd' },
{ value: _duration.hours(), unit: 'h' },
{ value: _duration.minutes(), unit: 'min' },
{ value: _duration.seconds(), unit: 's' },
];
const durationArr = duration.filter((item) => item.value) || [];
const durationStr = durationArr.map((item) => `${item.value}${item.unit}`).join('');
Comp = tip ? <Tooltip title={tip}>{durationStr}</Tooltip> : durationStr;
}
}
break;
case 'progressBar':
{
const { barCompletedNum, barTotalNum, barPercent, text, status, tip } = data;
const value = barPercent || (barCompletedNum / barTotalNum) * 100 || 0;
const content = (
<>
<Progress
percent={value}
type="circle"
width={20}
strokeWidth={18}
format={() => null}
strokeColor={statusColorMap[status]}
/>
<span className="text-black-8 ml-2">{text}</span>
</>
);
Comp = tip ? <Tooltip title={tip}>{content}</Tooltip> : content;
}
break;
case 'user':
const curUsers = [];
if (isArray(data.id)) {
data.id.forEach((vItem: any) => {
curUsers.push(userMap[vItem] || {});
});
} else {
curUsers.push(userMap[data.id] || {});
}
if (data.showIcon === false) {
Comp = map(curUsers, (item) => item.nick || item.name || item.id || i18n.t('common:None')).join(', ');
} else {
Comp = (
<div>
{map(curUsers, (cU, idx) => {
return (
<span key={idx}>
{data.showIcon === false ? null : (
<Avatar src={cU.avatar} size="small">
{cU.nick ? getAvatarChars(cU.nick) : i18n.t('None')}
</Avatar>
)}
<span className="ml-0.5 mr-1" title={cU.name}>
{cU.nick || cU.name || data.value || i18n.t('common:None')}
</span>
</span>
);
})}
</div>
);
}
break;
case 'dropDownMenu':
// {
// const {menus, operations} = data || {};
// Comp = <DropdownSelectNew options={} />
// }
break;
case 'text':
if (typeof data === 'string') {
Comp = data;
} else if (typeof data === 'object') {
const { text, enableCopy, status, showDot = false, tip, onlyText = false } = data;
let value = status ? <Badge text={text} status={status} showDot={showDot} onlyText={onlyText} /> : text;
if (tip) {
value = (
<Tooltip overlayClassName="whitespace-pre" title={tip} className="truncate w-full inline-block">
{value}
</Tooltip>
);
} else {
value = <Ellipsis title={value} />;
}
Comp = enableCopy ? (
<span className="flex group">
<span className="ant-table-cell-ellipsis group-hover:text-purple-deep" title={text}>
{text}
</span>
<Copy>
<ErdaIcon
type="fz1"
size={12}
data-clipboard-text={text}
onClick={(e) => e.stopPropagation()}
className="ml-1 cursor-copy text-desc opacity-0 group-hover:text-purple-deep group-hover:opacity-100"
/>
</Copy>
</span>
) : (
value
);
}
break;
case 'labels':
{
const { labels, showCount } = data;
// TODO showCount should be calculated based on the container width
Comp = (
<TagsRow
labels={labels.map((item: { id: string; title?: string; color?: string; group?: string }) => ({
...item,
label: item.title || item.id,
}))}
showCount={showCount ?? 2}
/>
);
}
break;
case 'moreOperations':
{
const { ops } = data;
const getIcon = (icon: { type?: string; url?: string }) => {
if (icon.type) {
return <ErdaIcon type={icon.type} color="currentColor" className="mr-1 text-white-6" />;
}
if (icon.url) {
return <img src={icon.url} />;
}
return null;
};
const getTableOperationItem = (op: CP_COMMON.Operation, key: string, record: Obj) => {
const { confirm, disabled, disabledTip, text, icon, operations } = op;
const { click } = operations || {};
const { serverData } = click || {};
if (disabled === true) {
// 无权限操作
return (
<Menu.Item key={key} className="p-0">
<WithAuth noAuthTip={disabledTip} key={key} pass={false}>
<span className="table-operations-btn px-4 py-1 block flex-h-center">
{icon ? getIcon(icon) : null}
{text}
</span>
</WithAuth>
</Menu.Item>
);
} else if (confirm) {
// 需要确认的操作
return (
<Menu.Item key={key} className="p-0 bg-transparent">
<Popconfirm
title={confirm}
onConfirm={(e) => {
e && e.stopPropagation();
extra?.execOperation({
key: 'click',
...click,
clientData: {
dataRef: op,
parentDataRef: record,
},
serverData,
});
const customFunc = get(extra, `customOp.operations.${key}`);
if (customFunc) {
customFunc(op, record);
}
}}
key={key}
onCancel={(e: any) => e && e.stopPropagation()}
zIndex={1100}
>
<span
className="table-operations-btn px-4 py-1 block flex-h-center text-white-9"
onClick={(e: any) => e.stopPropagation()}
>
{icon ? getIcon(icon) : null}
{text}
</span>
</Popconfirm>
</Menu.Item>
);
} else {
// 普通的操作
return (
<Menu.Item key={key} className="p-0">
<span
className="table-operations-btn px-4 py-1 block flex-h-center text-white-9"
key={key}
onClick={(e: any) => {
e.stopPropagation();
extra?.execOperation({
key: 'click',
...click,
clientData: {
dataRef: op,
parentDataRef: record,
},
serverData,
});
const customFunc = get(extra, `customOp.operations.${key}`);
if (customFunc) {
customFunc(op, record);
}
}}
>
{icon ? getIcon(icon) : null}
{text}
</span>
</Menu.Item>
);
}
};
const operationList = [] as any[];
if (ops) {
// 根据配置的operations展示
const operations = sortBy(
filter(map(ops) || [], (item: CP_COMMON.Operation) => item.show !== false),
'showIndex',
);
map(operations, (item: CP_COMMON.Operation) => {
if (item) {
operationList.push(getTableOperationItem(item, item.id, record));
}
});
}
if (!operationList.length) {
Comp = null;
} else {
Comp = (
<div className="table-operations">
<Dropdown
overlay={
<Menu theme="dark" style={{ minWidth: 160, padding: '8px 0' }}>
{operationList}
</Menu>
}
align={{ offset: [0, 5] }}
trigger={['click']}
overlayStyle={{ zIndex: 1040 }}
>
<ErdaIcon
type="more"
className="cursor-pointer p-1 bg-hover rounded-sm"
onClick={(e) => e.stopPropagation()}
/>
</Dropdown>
</div>
);
}
}
break;
default:
Comp = (typeof data === 'string' ? data : data?.text) || '-';
break;
}
return Comp;
}
Example #15
Source File: ApplyPage.tsx From office-hours with GNU General Public License v3.0 | 5 votes |
ProgressBar = styled(Progress)`
padding-bottom: 2.5em;
& .ant-progress-inner {
border: 1px solid #d9d9d9;
}
`
Example #16
Source File: ProgressBar.tsx From jmix-frontend with Apache License 2.0 | 5 votes |
export function ProgressBar({ ...props }: ProgressProps) {
return <Progress {...props} />;
}
Example #17
Source File: Storage.tsx From whiteboard-demo with MIT License | 5 votes |
private renderZipCell(downloader: DownloadLogic, pptState: PPTState): React.ReactNode {
const { t } = this.props
const displayProgress = (
pptState.phase === TaskPhase.Downloading
);
const enableRemoveCache = (
this.state.mode === DownloadingMode.Freedom &&
pptState.phase === TaskPhase.Cached
);
return (
<div key={pptState.uuid}>
<div className="room-cell-box">
<div className="room-cell-left">
<div className="room-cell-image">
<img src={zip_icon} alt={"cover"} />
{displayProgress &&
<div className="room-cell-image-cover">
<Progress
width={42}
style={{color: "white"}}
strokeWidth={6}
type="circle"
trailColor={"white"}
percent={pptState.progress} />
</div>
}
</div>
<div>
<div className="room-cell-text">{pptState.name}</div>
</div>
</div>
<div className="room-download-cell-right">
{this.renderDownloadButton(downloader, pptState)}
<Button
onClick={() => downloader.removeTask(pptState.uuid)}
disabled={!enableRemoveCache}
style={{width: 96}}>
{t('delete')}
</Button>
</div>
</div>
<div className="room-cell-cut-line" />
</div>
);
}
Example #18
Source File: StatsPageContent.tsx From disco-cube-admin with MIT License | 5 votes |
StatsPageContent: React.FC<Props> = ({
isConnected,
statusChangedAt,
cpuLoadsPercent,
allSystemInfo,
memUsagePercent,
cpuTemperature,
}) => {
return (
<Segment spacing={10} width="100%" maxWidth={500} height={"100%"} scroll="vertical">
<h1>Disco Cube</h1>
<Vertical spacing={30}>
<Horizontal spacing={40}>
<Stat label="Status">
<div>
<span style={{ color: isConnected ? "#3f8600" : "#cf1322" }}>
{isConnected ? "Connected" : "Disconnected"}
</span>
<span style={{ color: "rgba(0, 0, 0, 0.45)", fontSize: "0.75em" }}>
{statusChangedAt ? " " + formatDistanceToNow(statusChangedAt) + " ago" : ""}
</span>
</div>
</Stat>
<Stat label="CPU temperature">
<span>{cpuTemperature}</span>
</Stat>
</Horizontal>
<Horizontal spacing={40}>
<Stat label="CPU LOADS">
<Vertical>
{cpuLoadsPercent.map((c, i) => (
<Progress
key={i}
percent={Math.round(c)}
strokeColor={{
from: getProgressStrokeColorFromPercentage(c),
to: getProgressStrokeColorFromPercentage(c),
}}
/>
))}
</Vertical>
</Stat>
<Stat label="Mem usage">
<Progress
type="circle"
width={80}
percent={Math.round(memUsagePercent)}
strokeColor={{
from: getProgressStrokeColorFromPercentage(memUsagePercent),
to: getProgressStrokeColorFromPercentage(memUsagePercent),
}}
/>
</Stat>
<Stretch />
</Horizontal>
<Horizontal spacing={40}>
<Stat label="All System Info" width={360}>
<SystemInfoTree info={allSystemInfo} />
</Stat>
</Horizontal>
</Vertical>
</Segment>
);
}
Example #19
Source File: components.tsx From jitsu with MIT License | 5 votes |
render() {
return <Progress type="circle" percent={this.state.progressPercents} />
}
Example #20
Source File: form.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
CustomUpload = ({ form, onChange }: { form: { form: FormInstance }; onChange: (value?: string) => void }) => {
const [fileList, setFileList] = React.useState<UploadFile[]>([]);
const fileType = 'application/zip';
const props: UploadProps = {
fileList,
accept: '.zip',
beforeUpload: (file) => {
const reg = new RegExp(/^.*\.zip$/);
if (!reg.test(file.name)) {
onChange('type-error');
return false;
}
return true;
},
onChange: async ({ file }) => {
if (file.type !== fileType) {
setFileList([{ ...file, status: 'error', percent: 100, name: file.name }]);
} else {
setFileList([file]);
if (file.response?.success) {
const diceFileID = file.response?.data.uuid;
const res = await parseVersion.fetch({ diceFileID });
if (res.success) {
onChange(diceFileID);
form.form.setFieldsValue({ version: res.data?.version });
}
}
}
},
};
const remove = () => {
setFileList([]);
onChange();
};
return (
<div className="w-1/2">
<Dragger {...getUploadProps(props, 10)}>
<div className="flex-all-center py-1">
<img src={EmptySVG} style={{ height: 80 }} />
<div className="ml-2.5">
<div className="text-left text-default text-base">{i18n.t('dop:Upload ZIP file')}</div>
<div className="text-xs text-default-6 leading-5">{i18n.t('dop:Click to browse and upload')}</div>
</div>
</div>
</Dragger>
{fileList.map((item) => (
<div className="mt-4">
<div className="flex-h-center justify-between">
{item.name}
<ErdaIcon type="guanbi" className="text-default-4 cursor-pointer" onClick={remove} />
</div>
<Progress showInfo={false} percent={item.percent || 0} status={progressStatusMap[item.status]} />
</div>
))}
</div>
);
}
Example #21
Source File: common-comp.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
BaseInfo = () => {
const planItemDetail = testPlanStore.useStore((s) => s.planItemDetail);
const partnerIDs = planItemDetail.partnerIDs || [];
const percent = React.useMemo(() => {
const { succ, total } = planItemDetail.relsCount;
return Math.floor((succ / total) * 100 || 0);
}, [planItemDetail.relsCount]);
const createTime = planItemDetail.createdAt ? moment(planItemDetail.createdAt).format('YYYY-MM-DD HH:mm:ss') : '';
const content = (
<div>
<span className="text-normal font-medium mb-2">{i18n.t('dop:Participant')}</span>
<div className="flex flex-wrap items-center participant-items justify-start">
{partnerIDs.map((value, index) => {
return (
<span key={`${String(index)}-${value}`} className="mr-2 mb-2">
<UserInfo.RenderWithAvatar id={value} />
</span>
);
})}
</div>
</div>
);
return (
<div className="common-list-item px-0">
<div>
<div className="title">
{planItemDetail.id} - {planItemDetail.name}
</div>
<div className="sub member">
<span className="ml-1">{i18n.t('dop:Principal')}:</span>
<UserInfo.RenderWithAvatar id={planItemDetail.ownerID} />
<span className="ml-6">{i18n.t('dop:Participant')}:</span>
<Popover overlayStyle={{ width: 280 }} overlayClassName="participant-popover" content={content}>
<span className="participant flex justify-between items-center hover-active">
{partnerIDs.slice(0, 4).map((p, index) => (
<UserInfo.RenderWithAvatar showName={false} id={p} key={`${String(index)}-${p}`} />
))}
{partnerIDs.length > 4 ? <span className="count px-1 font-medium">+{partnerIDs.length - 4}</span> : null}
</span>
</Popover>
{/* <span>{planItemDetail.relatedIterative} 迭代</span> */}
</div>
</div>
<div>
<div className="text-normal">
<Progress strokeWidth={12} style={{ width: '230px' }} percent={percent} showInfo={false} />{' '}
{i18n.t('dop:Pass rate')} {percent}%
</div>
<div className="sub float-right">
{<UserInfo id={planItemDetail.creatorID} />} {i18n.t('dop:built in')} {createTime}
</div>
</div>
</div>
);
}
Example #22
Source File: BasicProgress.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
export function BasicProgress(props: BasicProgressProps): React.ReactElement {
const [color, setColor] = useState(props.configProps?.strokeColor || "blue");
useEffect(() => {
if (props.colorMap) {
const curObj = props.colorMap.find((item) => {
return +item.progress >= +props.value;
});
const color = Object.values(Color).includes(curObj?.color)
? curObj.color
: "";
setColor(color);
}
}, [props.colorMap, props.value]);
const format = (percent) => {
return (
<div className={style.showContainer} style={{ fontSize: props.fontSize }}>
<span
style={{ color: props.textColor ?? `var(--theme-${color}-color)` }}
className={style.showValue}
>
{props.text}
</span>
<span className={style.showDescription}>{props.description}</span>
</div>
);
};
return (
<div className={style.mainContainer}>
<Progress
type={props.type}
strokeColor={`var(--theme-${color}-color)`}
percent={props.value}
format={format}
{...props.configProps}
/>
</div>
);
}
Example #23
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
CP_TopN: React.FC<CP_DATA_RANK.Props> = (props) => {
const {
customOp,
props: configProps,
data: { list },
} = props || {};
const handleClick = (item: CP_DATA_RANK.IItem) => {
customOp?.clickRow?.(item);
};
return (
<div className="cp-data-rank h-full">
<Row gutter={8} {...configProps.rowsProps} className="h-full">
{(list ?? []).map((listItem, index) => {
const { color = themeColor.default, titleIcon, backgroundIcon } = configProps.theme?.[index] || {};
const { title, items, span } = listItem;
return (
<Col key={title} span={span} className="my-1">
<div
className="px-4 py-3 relative h-full items-wrapper"
style={{ backgroundColor: colorToRgb(color, 0.04) }}
>
<div
className="absolute top-0 right-0 bg-icon-wrapper flex justify-center items-center"
style={{ color: colorToRgb(color, 0.1) }}
>
{titleIcon || backgroundIcon ? <ErdaIcon size={44} type={backgroundIcon ?? titleIcon} /> : null}
</div>
<div className="mb-2 flex justify-start items-center">
{titleIcon ? (
<span style={{ color }} className="mr-2 flex items-center">
<ErdaIcon size={16} type={titleIcon} />
</span>
) : null}
<p className="mb-0 flex-1 text-purple-dark font-medium overflow-hidden overflow-ellipsis whitespace-nowrap text-purple-dark ">
{title}
</p>
</div>
<div>
{!items?.length ? (
<EmptyHoder relative />
) : (
items?.map((item) => {
const { name, value, unit, id, percent } = item;
return (
<div
key={id}
className={`${customOp?.clickRow ? 'cursor-pointer' : ''} flex flex-col mb-2 last:mb-0`}
onClick={() => {
handleClick(item);
}}
>
<div className="flex py-1">
<Ellipsis className="flex-1 text-purple-dark" title={name} />
<div className="ml-2">
<span className="text-purple-dark">{value}</span>
{unit ? <span className="text-sub text-xs ml-0.5">{unit}</span> : null}
</div>
</div>
<Progress strokeColor={color} percent={percent} showInfo={false} strokeWidth={4} />
</div>
);
})
)}
</div>
</div>
</Col>
);
})}
</Row>
</div>
);
}
Example #24
Source File: index.tsx From nebula-studio with Apache License 2.0 | 4 votes |
TaskItem = (props: IProps) => {
const {
data: {
space,
id,
name,
stats: { totalImportedBytes, totalBytes, numFailed, numReadFailed },
status,
message,
updateTime,
createTime
},
showConfigDownload,
onViewLog,
onConfigDownload,
onTaskStop,
onTaskDelete } = props;
const [progressStatus, setStatus] = useState<'success' | 'active' | 'normal' | 'exception' | undefined>(undefined);
const [extraMsg, setExtraMsg] = useState('');
const addMsg = () => {
const info: string[] = [];
if(numFailed > 0) {
info.push(intl.get('import.notImported', { total: numFailed }));
}
if(numReadFailed > 0) {
info.push(intl.get('import.readFailed', { total: numReadFailed }));
}
info.length > 0 && setExtraMsg(info.join(', '));
};
useEffect(() => {
if(status === ITaskStatus.StatusFinished) {
setStatus('success');
addMsg();
} else if(status === ITaskStatus.StatusProcessing) {
setStatus('active');
addMsg();
} else {
setStatus('exception');
if(message) {
setExtraMsg(message);
}
}
}, [status]);
return (
<div className={styles.taskItem}>
<div className={styles.row}>
<span>{intl.get('common.space')}: {space}</span>
{showConfigDownload && <Button type="link" size="small" onClick={() => onConfigDownload(id)}>
<Icon type="icon-studio-btn-download" />
{intl.get('import.downloadConfig')}
</Button>}
</div>
<div className={styles.row}>
<div className={styles.progress}>
<div className={styles.progressInfo}>
<span className={styles.taskName}>
{name}
{status === ITaskStatus.StatusFinished && <span className={styles.completeInfo}>
<CheckCircleFilled />
{intl.get('import.importCompleted')}
<span className={styles.red}>{extraMsg && ` (${extraMsg})`}</span>
</span>}
{status === ITaskStatus.StatusAborted && <span className={styles.errInfo}>
{intl.get('import.importFailed')}
{extraMsg && ` (${extraMsg})`}
</span>}
{status === ITaskStatus.StatusStoped && <span className={styles.errInfo}>
{intl.get('import.importStopped')}
</span>}
</span>
<div className={styles.moreInfo}>
<span>
{status !== ITaskStatus.StatusFinished && `${getFileSize(totalImportedBytes)} / `}
{getFileSize(totalBytes)}{' '}
</span>
<span>{dayjs.duration(dayjs.unix(updateTime).diff(dayjs.unix(createTime))).format('HH:mm:ss')}</span>
</div>
</div>
<Progress
format={percent => `${percent}%`}
status={progressStatus}
percent={status !== ITaskStatus.StatusFinished ? floor(totalImportedBytes / totalBytes * 100, 2) : 100}
strokeColor={progressStatus && COLOR_MAP[progressStatus]} />
</div>
<div className={styles.operations}>
<Button className="primaryBtn" onClick={() => onViewLog(id, space, status)}>{intl.get('import.viewLogs')}</Button>
{status === ITaskStatus.StatusProcessing &&
<Popconfirm
placement="left"
title={intl.get('import.endImport')}
onConfirm={() => onTaskStop(id)}
okText={intl.get('common.confirm')}
cancelText={intl.get('common.cancel')}
>
<Button className="cancelBtn">{intl.get('import.endImport')}</Button>
</Popconfirm>}
{status !== ITaskStatus.StatusProcessing &&
<Popconfirm
placement="left"
title={intl.get('common.ask')}
onConfirm={() => onTaskDelete(id)}
okText={intl.get('common.confirm')}
cancelText={intl.get('common.cancel')}
>
<Button danger={true}>{intl.get('common.delete')}</Button>
</Popconfirm>}
</div>
</div>
</div>
);
}
Example #25
Source File: BatchExecuteByFilter.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
BatchExecuteByFilter: React.FC<BatchExecuteByFilterProp> = React.memo((props) => {
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [token, setToken] = useState(randomString(20))
const [executing, setExecuting] = useState(false);
const [percent, setPercent] = useState(0);
// 执行任务历史列表
const [taskHistory, setTaskHistory] = useState<NewTaskHistoryProps[]>([])
// 计算插件数量
useEffect(() => {
setLoading(true)
const result = simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag);
ipcRenderer.invoke("QueryYakScript", result).then((data: QueryYakScriptsResponse) => {
setTotal(data.Total)
}).catch(e => console.info(e))
.finally(() => setTimeout(() => setLoading(false), 300))
}, [
useDebounce(props.simpleQuery, {wait: 500}),
useDebounce(props.isAll, {wait: 500})
])
// 回复缓存
useEffect(() => {
setLoading(true)
ipcRenderer
.invoke("get-value", ExecuteTaskHistory)
.then((res: any) => {
setTaskHistory(res ? JSON.parse(res) : [])
})
.catch(() => {
})
.finally(() => {
setTimeout(() => setLoading(false), 300)
})
}, [])
// 执行批量任务
const run = useMemoizedFn((t: TargetRequest) => {
setPercent(0)
//@ts-ignore
const time = Date.parse(new Date()) / 1000
const obj: NewTaskHistoryProps = {
target: t,
simpleQuery: props.simpleQuery,
isAll: props.isAll,
time: formatTimestamp(time)
}
const arr = [...taskHistory]
if (taskHistory.length === 10) arr.pop()
arr.unshift(obj)
setTaskHistory(arr)
ipcRenderer.invoke("set-value", ExecuteTaskHistory, JSON.stringify(arr))
const tokens = randomString(40)
setToken(tokens)
StartExecBatchYakScriptWithFilter(
t, simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag),
tokens).then(() => {
setExecuting(true)
}).catch(e => {
failed(`启动批量执行插件失败:${e}`)
})
});
const cancel = useMemoizedFn(() => {
CancelBatchYakScript(token).then()
})
useEffect(() => {
return cancel()
}, [])
const executeHistory = useMemoizedFn((item: NewTaskHistoryProps) => {
setLoading(true)
props.executeHistory(item)
setTimeout(() => setLoading(false), 300);
})
useEffect(() => {
if (!token) return
ipcRenderer.on(`${token}-end`, async (e) => {
console.info("call finished by token filter")
setTimeout(() => setExecuting(false), 300)
})
return () => {
ipcRenderer.removeAllListeners(`${token}-end`)
}
}, [token])
return <AutoCard
title={<Space>
{"已选插件"}
<Tag>{`${total}`}</Tag>
</Space>}
size={"small"} bordered={false}
extra={<Space>
{(percent > 0 || executing) && <div style={{width: 200}}>
<Progress status={executing ? "active" : undefined} percent={
parseInt((percent * 100).toFixed(0))
}/>
</div>}
</Space>}
bodyStyle={{display: "flex", flexDirection: "column", padding: '0 5px', overflow: "hidden"}}
>
<ExecSelectedPlugins
disableStartButton={total <= 0}
onSubmit={run}
onCancel={cancel}
executing={executing}
loading={loading}
history={taskHistory}
executeHistory={executeHistory}
/>
<Divider style={{margin: 4}}/>
<div style={{flex: '1', overflow: "hidden"}}>
<BatchExecutorResultByFilter token={token} executing={executing} setPercent={setPercent}/>
</div>
</AutoCard>
})
Example #26
Source File: Entries.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
// eslint-disable-next-line complexity
export function Entries({
source,
isConfirmed,
isCancelled,
account,
loading,
totalCount,
currentPage,
onChangePage,
}: EntriesProps) {
const { t } = useTranslation();
const isInjected = useIsInjected();
const { network } = useApi();
const renderAction = useCallback(
// eslint-disable-next-line complexity
(row: Entry) => {
if (row.status) {
return <span>{t(`status.${row.status}`)}</span>;
}
const actions: TxActionType[] = [];
// eslint-disable-next-line react/prop-types
const pairs = (account.meta?.addressPair ?? []) as AddressPair[];
const injectedAccounts: string[] = pairs.filter((pair) => isInjected(pair.address)).map((pair) => pair.address);
if (injectedAccounts.includes(row.depositor)) {
actions.push('cancel');
}
const localAccountInMultisigPairList = intersection(
injectedAccounts,
pairs.map((pair) => pair.address)
);
const approvedLocalAccounts = intersection(localAccountInMultisigPairList, row.approvals);
if (approvedLocalAccounts.length !== localAccountInMultisigPairList.length) {
actions.push('approve');
}
if (actions.length === 0) {
// eslint-disable-next-line react/prop-types
if (row.approvals && row.approvals.length === account.meta.threshold) {
actions.push('pending');
}
}
return (
<Space>
{actions.map((action) => {
if (action === 'pending') {
return (
<Button key={action} disabled>
{t(action)}
</Button>
);
} else if (action === 'approve') {
return <TxApprove key={action} entry={row} />;
} else {
return <TxCancel key={action} entry={row} />;
}
})}
</Space>
);
},
[account.meta?.addressPair, account.meta.threshold, isInjected, t]
);
const columns: ColumnsType<Entry> = [
{
title: t(isConfirmed || isCancelled ? 'extrinsic_index' : 'call_data'),
dataIndex: isConfirmed || isCancelled ? 'extrinsicIdx' : 'hexCallData',
width: 300,
align: 'left',
// eslint-disable-next-line complexity
render(data: string) {
let extrinsicHeight = '';
let extrinsicIndex = '';
if ((isConfirmed || isCancelled) && data.split('-').length > 1) {
extrinsicHeight = data.split('-')[0];
extrinsicIndex = data.split('-')[1];
}
return !(isConfirmed || isCancelled) ? (
<>
<Typography.Text copyable={!isEmpty(data) && { text: data }}>
{!isEmpty(data)
? // ? `${data.substring(0, CALL_DATA_LENGTH)}${data.length > CALL_DATA_LENGTH ? '...' : ''}`
toShortString(data, CALL_DATA_LENGTH)
: '-'}
</Typography.Text>
</>
) : (
<SubscanLink extrinsic={{ height: extrinsicHeight, index: extrinsicIndex }}>{data}</SubscanLink>
);
},
},
{
title: t('actions'),
dataIndex: 'callDataJson',
align: 'left',
render: renderMethod,
},
{
title: t('progress'),
dataIndex: 'approvals',
align: 'left',
render(approvals: string[]) {
const cur = (approvals && approvals.length) || 0;
return cur + '/' + account.meta.threshold;
},
},
{
title: t('status.index'),
key: 'status',
align: 'left',
render: (_, row) => renderAction(row),
},
];
const expandedRowRender = (entry: Entry) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const progressColumnsNested: ColumnsType<any> = [
{ dataIndex: 'name', width: 100 },
{
width: 400,
dataIndex: 'address',
render: (address) => (
<Space size="middle">
<BaseIdentityIcon theme="polkadot" size={32} value={address} />
<SubscanLink address={address} copyable />
</Space>
),
},
{
width: 250,
key: 'status',
render: (_, pair) => renderMemberStatus(entry, pair, network, !isCancelled && !isConfirmed),
},
];
// const callDataJson = entry.callData?.toJSON() ?? {};
const args: Required<ArgObj>[] = ((entry.meta?.args ?? []) as Required<ArgObj>[]).map((arg) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = (entry.callDataJson?.args as any)[arg?.name ?? ''];
return { ...arg, value };
});
return (
<div className="record-expand bg-gray-100 py-3 px-5">
<div className=" text-black-800 text-base leading-none mb-3">{t('progress')}</div>
<div className="members">
<Table
columns={progressColumnsNested}
dataSource={account.meta.addressPair as { key: string; name: string; address: string }[]}
pagination={false}
bordered
rowKey="address"
showHeader={false}
className="mb-4 mx-4"
/>
</div>
<div className=" text-black-800 text-base leading-none my-3">{t('parameters')}</div>
<Args
args={args}
className="mb-4 mx-4"
section={entry.callDataJson?.section}
method={entry.callDataJson?.method}
/>
</div>
);
};
return (
<div className="record-table">
<Table
loading={loading}
dataSource={source}
columns={columns}
rowKey={(record) => record.callHash ?? (record.blockHash as string)}
pagination={
isConfirmed || isCancelled
? {
total: totalCount,
pageSize: 10,
current: currentPage,
onChange: onChangePage,
}
: false
}
expandable={{
expandedRowRender,
expandIcon: genExpandIcon(),
expandIconColumnIndex: 4,
}}
className="lg:block hidden"
></Table>
<Space direction="vertical" className="lg:hidden block">
{source.map((data) => {
const { address, hash, callData, approvals } = data;
const approvedCount = approvals.length || 0;
const threshold = (account.meta.threshold as number) || 1;
return (
<Collapse key={address} expandIcon={() => <></>} className="wallet-collapse">
<Panel
header={
<Space direction="vertical" className="w-full mb-4">
<Typography.Text className="mr-4" copyable>
{hash}
</Typography.Text>
<div className="flex items-center">
<Typography.Text>{renderMethod(callData)}</Typography.Text>
<Progress
/* eslint-disable-next-line no-magic-numbers */
percent={parseInt(String((approvedCount / threshold) * 100), 10)}
steps={threshold}
className="ml-4"
/>
</div>
</Space>
}
key={address}
extra={renderAction(data)}
className="overflow-hidden mb-4"
>
<MemberList
data={account}
statusRender={(pair) => renderMemberStatus(data, pair, network, !isCancelled && !isConfirmed)}
/>
</Panel>
</Collapse>
);
})}
{!source.length && <Empty />}
</Space>
</div>
);
}
Example #27
Source File: YakitUpgrade.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakitUpgrade: React.FC<YakitUpgradeProp> = (props) => {
const [currentVersion, setCurrentVersion] = useState("")
const [loading, setLoading] = useState(false);
const [latestLoading, setLatestLoading] = useState(false);
const [latestVersion, setLatestVersion] = useState("");
const [downloading, setDownloading] = useState(false);
const [downloadProgress, setDownloadProgress] = useState<DownloadingState>();
const queryLatestVersion = () => {
setLatestLoading(true)
ipcRenderer.invoke("query-latest-yakit-version").then((data: string) => {
if (data.startsWith("v")) {
data = data.substr(1)
}
setLatestVersion(data)
}).catch((e: any) => {
failed(`${e}`)
}).finally(
() => setTimeout(() => setLatestLoading(false), 300)
)
}
const updateCurrent = () => {
setLoading(true)
ipcRenderer.invoke("yakit-version").then((data: string) => {
setCurrentVersion(data)
}).catch((e: any) => {
failed(`${e}`)
}).finally(
() => setTimeout(() => setLoading(false), 300)
)
}
useEffect(() => {
ipcRenderer.on("download-yakit-engine-progress", async (e: any, state: DownloadingState) => {
setDownloadProgress(state);
})
return () => {
ipcRenderer.removeAllListeners("download-yakit-engine-progress")
}
}, [])
useEffect(() => {
updateCurrent()
queryLatestVersion()
// ipcRenderer.invoke("get-windows-install-dir").then(setWinPath).catch(() => {
// }).finally()
}, [])
const install = (version: string) => {
Modal.confirm({
title: "Yakit 下载完毕",
width: "50%",
content: <>
<Space direction={"vertical"}>
<Tag color={"purple"}>Yakit 安装包下载完毕</Tag>
<p/>
<Tag>选择 Ok/确定 允许打开 Yakit 安装包下载目录,用户双击安装</Tag>
<Tag>选择 Cancel 用户自行找到安装包</Tag>
<br/>
<Tag>linux/macOs 安装包存储在:~/yakit-projects/yak-engine</Tag>
<Tag>windows 安装包存储在:%HOME%/yakit-projects/yak-engine</Tag>
</Space>
</>,
onOk: () => {
ipcRenderer.invoke("install-yakit", latestVersion).then(() => {
}).catch((err: any) => {
})
}
})
}
const isLatest = currentVersion === latestVersion;
const color = isLatest ? "green" : "red";
return <Card
size={"small"} bodyStyle={{padding: 0}} bordered={false}
>
<Space direction={"vertical"} style={{width: "100%"}}>
<Spin spinning={loading}>
<Alert message={<Space>
当前 Yakit 版本:
<Tag
color={color}
>{currentVersion}</Tag>
{isLatest ? <Tag color={"green"}>已是最新</Tag> : <Tag
color={"red"}
>Yakit 需要更新</Tag>}
</Space>}/>
</Spin>
<Spin spinning={loading}>
<Alert
type={"success"}
message={<Space>
Yakit 最新版本为:
<Tag color={"green"}>{latestVersion}</Tag>
</Space>}/>
</Spin>
<Spin spinning={downloading}>
<Space>
<Popconfirm
visible={(isLatest || loading || latestLoading) ? false : undefined}
title={`确定要更新版本: ${latestVersion}`}
onConfirm={e => {
setDownloading(true)
ipcRenderer.invoke("download-latest-yakit", latestVersion).then(() => {
success("下载完毕")
install(latestVersion)
}).catch((e: any) => {
failed("下载失败")
}).finally(() => {
setTimeout(() => setDownloading(false), 100)
})
}}
>
<Button
type={"primary"} disabled={isLatest || loading || latestLoading}
>
一键下载最新版 Yakit
</Button>
</Popconfirm>
<Button type={"link"} onClick={() => {
install(latestVersion)
}}>我已经下载,点此安装</Button>
</Space>
</Spin>
{downloadProgress && <Progress percent={
downloading ? Math.floor((downloadProgress?.percent || 0) * 100) : 100
}/>}
{downloadProgress && downloading && <Space>
<Tag>剩余时间:{downloadProgress?.time.remaining}</Tag>
<Tag>已下载用时:{downloadProgress?.time.elapsed}</Tag>
<Tag>
下载速度:约{((downloadProgress?.speed || 0) / 1000000).toFixed(2)}M/s
</Tag>
</Space>}
</Space>
</Card>
}
Example #28
Source File: BatchExecutorPage.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
BatchExecutorPage: React.FC<BatchExecutorPageProp> = (props) => {
const [loading, setLoading] = useState(false);
const [pluginType, setPluginType] = useState<"yak" | "nuclei" | string>("yak");
const [limit, setLimit] = useState(200);
const [keyword, setKeyword] = useState("");
const [scripts, setScripts, getScripts] = useGetState<YakScript[]>([]);
const [total, setTotal] = useState(0);
const [selected, setSelected] = useState<string[]>([]);
const [indeterminate, setIndeterminate] = useState(false);
const [checked, setChecked] = useState(false)
const [executing, setExecuting] = useState(false);
const [token, setToken] = useState<string>(randomString(40));
const [percent, setPercent] = useState(0.0);
// 处理性能问题
const containerRef = useRef();
const wrapperRef = useRef();
const [list] = useVirtualList(getScripts(), {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: 50, overscan: 20,
})
const [vlistHeigth, setVListHeight] = useState(600);
// 执行任务历史列表
const [taskHistory, setTaskHistory] = useState<TaskHistoryProps[]>([])
useEffect(() => {
setLoading(true)
ipcRenderer
.invoke("get-value", ExecuteTaskHistory)
.then((res: any) => {
setTaskHistory(res ? JSON.parse(res) : [])
})
.catch(() => {
})
.finally(() => {
setTimeout(() => setLoading(false), 300)
})
}, [])
useEffect(() => {
const totalYakScript = scripts.length;
const filterArr = scripts.filter((item) => selected.indexOf(item.ScriptName) > -1)
const IndeterminateFlag =
(filterArr.length > 0 && filterArr.length < totalYakScript && selected.length !== 0) ||
(filterArr.length === 0 && selected.length !== 0)
const checkedFlag = filterArr.length === totalYakScript && selected.length !== 0
setIndeterminate(IndeterminateFlag)
setChecked(checkedFlag)
}, [selected, scripts])
const search = useMemoizedFn(() => {
setLoading(true)
queryYakScriptList(
pluginType,
(data, total) => {
setTotal(total || 0)
setScripts(data)
}, () => setTimeout(() => setLoading(false), 300),
limit, undefined, keyword,
(pluginType === "yak" ? {
IsBatch: true
} : {
ExcludeNucleiWorkflow: true,
}) as any,
)
})
useEffect(() => {
setSelected([]);
if (!pluginType) return;
search()
}, [pluginType])
const selectYakScript = useMemoizedFn((y: YakScript) => {
if (!selected.includes(y.ScriptName)) {
setSelected([...selected, y.ScriptName])
}
});
const unselectYakScript = useMemoizedFn((y: YakScript) => {
setSelected(selected.filter(i => i !== y.ScriptName))
})
const renderListItem = useMemoizedFn((y: YakScript) => {
return <YakScriptWithCheckboxLine
key={y.ScriptName}
selected={selected.includes(y.ScriptName)} plugin={y} onSelected={selectYakScript}
onUnselected={unselectYakScript}
/>
});
const run = useMemoizedFn((t: TargetRequest) => {
setPercent(0)
//@ts-ignore
const time = Date.parse(new Date()) / 1000
const obj: TaskHistoryProps = {
target: t,
selected: selected,
pluginType: pluginType,
limit: limit,
keyword: keyword || "",
time: formatTimestamp(time)
}
const arr = [...taskHistory]
if (taskHistory.length === 10) arr.pop()
arr.unshift(obj)
setTaskHistory(arr)
ipcRenderer.invoke("set-value", ExecuteTaskHistory, JSON.stringify(arr))
const tokens = randomString(40)
setToken(tokens)
StartExecBatchYakScript(t, selected, tokens).then(() => {
setExecuting(true)
}).catch(e => {
failed(`启动批量执行插件失败:${e}`)
})
});
const cancel = useMemoizedFn(() => {
CancelBatchYakScript(token).then()
});
useEffect(() => {
ipcRenderer.on(`${token}-data`, async (e, data: any) => {
try {
if (data.ProgressMessage) {
setPercent(data.ProgressPercent)
return
}
} catch (e) {
console.info(e)
}
})
ipcRenderer.on(`${token}-error`, async (e, data) => {
failed(`批量执行插件遇到问题: ${data}`)
})
ipcRenderer.on(`${token}-end`, async (e) => {
setTimeout(() => setExecuting(false), 300)
})
return () => {
ipcRenderer.removeAllListeners(`${token}-data`)
ipcRenderer.removeAllListeners(`${token}-error`)
ipcRenderer.removeAllListeners(`${token}-end`)
}
}, [token])
const executeHistory = useMemoizedFn((item: TaskHistoryProps) => {
setLoading(true)
setLimit(item.limit)
setKeyword(item.keyword)
if (item.pluginType === pluginType) setTimeout(() => search(), 300);
else setPluginType(item.pluginType)
setTimeout(() => {
setSelected(item.selected)
setLoading(false)
}, 300);
})
return <div style={{width: "100%", height: "100%", display: "flex", overflowY: "hidden"}}>
<div style={{width: 470, height: "100%"}}>
{/*<AutoSpin*/}
{/* spinning={loading}*/}
{/*>*/}
<AutoCard
size={"small"}
bordered={false}
title={<Space>
<SelectOne label={"插件"} formItemStyle={{marginBottom: 0}} size={"small"} data={[
{text: "YAK 插件", value: "yak"},
{text: "YAML POC", value: "nuclei"},
]} value={pluginType} setValue={setPluginType}/>
</Space>}
bodyStyle={{
paddingLeft: 4,
paddingRight: 4,
overflow: "hidden", display: "flex", flexDirection: "column",
}}
extra={<Space>
<Popover title={"额外设置"} trigger={["click"]} content={<div>
<Form size={"small"} onSubmitCapture={e => {
e.preventDefault()
search()
}}>
<InputInteger
label={"插件展示数量"} value={limit} setValue={setLimit}
formItemStyle={{marginBottom: 4}}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type="primary" htmlType="submit">刷新</Button>
</Form.Item>
</Form>
</div>}>
<Button size={"small"} icon={<SettingOutlined/>} type={"link"}/>
</Popover>
<Popover title={"搜索插件关键字"} trigger={["click"]} content={<div>
<Form size={"small"} onSubmitCapture={e => {
e.preventDefault()
search()
}}>
<InputItem
label={""}
extraFormItemProps={{style: {marginBottom: 4}, colon: false}}
value={keyword}
setValue={setKeyword}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type="primary" htmlType="submit">搜索</Button>
</Form.Item>
</Form>
</div>}>
<Button size={"small"} type={!!keyword ? "primary" : "link"} icon={<SearchOutlined/>}/>
</Popover>
<Checkbox indeterminate={indeterminate} onChange={(r) => {
if (r.target.checked) {
const newSelected = [...scripts.map(i => i.ScriptName), ...selected];
setSelected(newSelected.filter((e, index) => newSelected.indexOf(e) === index));
} else {
setSelected([]);
}
}} checked={checked}>
全选
</Checkbox>
</Space>}
>
<div style={{flex: "1", overflow: "hidden"}}>
<ReactResizeDetector
onResize={(width, height) => {
if (!width || !height) {
return
}
setVListHeight(height)
}}
handleWidth={true} handleHeight={true} refreshMode={"debounce"} refreshRate={50}
/>
<div ref={containerRef as any} style={{height: vlistHeigth, overflow: "auto"}}>
<div ref={wrapperRef as any}>
{list.map(i => renderListItem(i.data))}
</div>
</div>
</div>
</AutoCard>
{/*</AutoSpin>*/}
</div>
<div style={{marginLeft: 12, flex: 1, backgroundColor: "#fff", overflow: "hidden"}}>
<AutoCard
title={<Space>
{"已选插件 / 当页插件 / 插件总量"}
<Tag>{`${selected.length} / ${scripts.length} / ${total}`}</Tag>
</Space>}
size={"small"} bordered={false}
extra={<Space>
{(percent > 0 || executing) && <div style={{width: 200}}>
<Progress status={executing ? "active" : undefined} percent={
parseInt((percent * 100).toFixed(0))
}/>
</div>}
</Space>}
bodyStyle={{display: "flex", flexDirection: "column", padding: '0 5px', overflow: "hidden"}}
>
{/* <ExecSelectedPlugins
disableStartButton={selected.length === 0}
onSubmit={run}
onCancel={cancel}
executing={executing}
loading={loading}
history={taskHistory}
executeHistory={executeHistory}
/> */}
<Divider style={{margin: 4}}/>
<div style={{flex: '1', overflow: "hidden"}}>
<AutoCard style={{padding: 4}} bodyStyle={{padding: 4, overflow: "hidden"}} bordered={false}>
<BatchExecutorResultUI token={token} executing={executing}/>
</AutoCard>
</div>
</AutoCard>
</div>
</div>
}
Example #29
Source File: YakUpgrade.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakUpgrade: React.FC<YakUpgradeProp> = (props) => {
const [currentVersion, setCurrentVersion] = useState("")
const [loading, setLoading] = useState(false);
const [latestLoading, setLatestLoading] = useState(false);
const [latestVersion, setLatestVersion] = useState("");
const [downloading, setDownloading] = useState(false);
const [downloadProgress, setDownloadProgress] = useState<DownloadingState>();
const [winPath, setWinPath] = useState("");
const [platformArch, setPlatformArch] = useState("");
const latestVersionWithoutV = latestVersion.startsWith("v") ? latestVersion.slice(1) : latestVersion;
const queryLatestVersion = () => {
setLatestLoading(true)
ipcRenderer.invoke("query-latest-yak-version").then((data: string) => {
setLatestVersion(data)
}).catch((e: any) => {
failed(`${e}`)
}).finally(
() => setTimeout(() => setLatestLoading(false), 300)
)
}
const updateCurrent = () => {
setLoading(true)
ipcRenderer.invoke("get-current-yak").then((data: string) => {
setCurrentVersion(data)
}).catch((e: any) => {
setCurrentVersion("")
failed(<>
获取 Yak 引擎当前版本失败,请按提示安装即可<Popover content={`${e}`}>
<Button size={"small"} type={"link"}>错误详情</Button>
</Popover>
</>)
}).finally(
() => setTimeout(() => setLoading(false), 300)
)
}
useEffect(() => {
ipcRenderer.invoke("get-platform-and-arch").then((e: string) => {
setPlatformArch(e)
})
ipcRenderer.on("download-yak-engine-progress", async (e: any, state: DownloadingState) => {
setDownloadProgress(state);
})
return () => {
ipcRenderer.removeAllListeners("download-yak-engine-progress")
}
}, [])
useEffect(() => {
updateCurrent()
queryLatestVersion();
ipcRenderer.invoke("get-windows-install-dir").then(setWinPath).catch(() => {
}).finally()
}, [])
useEffect(() => {
(props.existed || []).forEach(i => {
ipcRenderer.invoke("kill-yak-grpc", i.pid).then(() => {
info(`KILL yak PROCESS: ${i.pid}`)
})
})
}, [props.existed])
const install = (version: string) => {
Modal.confirm({
title: "Yak 核心引擎下载完毕,将会自动更新到系统目录",
width: "40%",
content: <>
<Space direction={"vertical"}>
{(platformArch.startsWith("darwin-") || platformArch === "") &&
<Tag color={"purple"}>*nix 系统下会安装在 /usr/local/bin/yak </Tag>}
{platformArch.startsWith("darwin-arm64") &&
<Space direction={"vertical"}>
<Tag color={"purple"}>macOS m1(pro/max) 用户需要检查 Rosetta 2 环境,如需要手动安装,如下:</Tag>
<Text mark={false} code={true} copyable={true}>softwareupdate --install-rosetta</Text>
</Space>
}
{platformArch.startsWith("win") && <Tag color={""}>windows 系统下会安装在 {winPath} </Tag>}
<p/>
<Tag>选择 Ok 允许 Yakit 操作</Tag>
<Tag>选择 Cancel 用户可以手动更新 %PATH%</Tag>
</Space>
</>,
onOk: () => {
ipcRenderer.invoke("install-yak-engine", latestVersion).then(() => {
success("安装成功,如未生效,重启 Yakit 即可")
}).catch((err: any) => {
failed(`安装失败: ${err}`)
}).finally(updateCurrent)
}
})
}
const isLatest = currentVersion === latestVersion;
const color = isLatest ? "green" : "red";
return <Card
size={"small"} bodyStyle={{padding: 0}} bordered={false}
>
<Space direction={"vertical"} style={{width: "100%"}}>
{platformArch === "darwin-arm64" && <Alert
type={"error"}
message={<>
当前系统为({platformArch}),如果未安装 Rosetta 2, 无法运行 Yak 核心引擎
<br/>
<br/>
<div>运行以下命令可手动安装 Rosetta,如已安装可忽略</div>
<Text mark={false} code={true} copyable={true}>softwareupdate --install-rosetta</Text>
</>}
/>}
<Spin spinning={loading}>
<Alert message={<Space>
当前本地安装的 Yak 核心引擎版本为:
<Tag
color={color}
>{currentVersion}</Tag>
{isLatest ? <Tag color={"green"}>已是最新</Tag> : <Tag
color={"red"}
>Yak 引擎需要更新</Tag>}
</Space>}/>
</Spin>
<Spin spinning={loading}>
<Alert
type={"success"}
message={<Space direction={"vertical"}>
<Space>
当前最新的 Yak 引擎版本为
<Tag color={"green"}>{latestVersion}</Tag>
</Space>
</Space>}/>
</Spin>
<Spin spinning={downloading}>
<Space>
<Popconfirm
visible={(isLatest || loading || latestLoading) ? false : undefined}
title={`确定要更新版本: ${latestVersion}`}
onConfirm={e => {
setDownloading(true)
ipcRenderer.invoke("download-latest-yak", latestVersion).then(() => {
success("下载完毕")
install(latestVersion)
}).catch((e: any) => {
failed("下载失败")
}).finally(() => {
setTimeout(() => setDownloading(false), 100)
})
}}
>
<Button
type={"primary"} disabled={isLatest || loading || latestLoading}
>
一键更新 Yak 引擎
</Button>
</Popconfirm>
<Button type={"link"} onClick={() => {
install(latestVersion)
}}>我已经下载,点此安装</Button>
<Button danger={true} size={"small"} onClick={() => {
showModal({
title: "yak 核心引擎下载链接",
width: "60%",
content: <Space direction={"vertical"}>
<Space>
Windows(x64) 下载:
<div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_windows_amd64.exe</div>
</Space>
<Space>
MacOS(intel/m1) 下载:
<div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_darwin_amd64</div>
</Space>
<Space>
Linux(x64) 下载:
<div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_linux_amd64</div>
</Space>
<Alert message={<div>
手动下载完成后 Windows 用户可以把引擎放在 %HOME%/yakit-projects/yak-engine/yak.exe 即可识别
<br/>
MacOS / Linux 用户可以把引擎放在 ~/yakit-projects/yak-engine/yak 即可识别
</div>}>
</Alert>
</Space>
})
}}>
网络问题无法下载?手动下载
</Button>
</Space>
</Spin>
{downloadProgress && <Progress percent={
downloading ? Math.floor((downloadProgress?.percent || 0) * 100) : 100
}/>}
{downloadProgress && downloading && <Space>
<Tag>剩余时间:{downloadProgress?.time.remaining}</Tag>
<Tag>已下载用时:{downloadProgress?.time.elapsed}</Tag>
<Tag>
下载速度:约{((downloadProgress?.speed || 0) / 1000000).toFixed(2)}M/s
</Tag>
</Space>}
</Space>
</Card>
}