feat:steps 里的 title、subTitle、description 支持变量 (#1800)

* feat:steps 里的 title、subTitle、description 支持变量

* format 强制使用 prittier
This commit is contained in:
吴多益 2021-04-14 11:11:04 +08:00 committed by GitHub
parent cc10cdb5e9
commit 649a59f28a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 175 additions and 138 deletions

View File

@ -1,5 +1,7 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"typescript.tsdk": "node_modules/typescript/lib",
"gitHistory.showEditorTitleMenuBarIcons": false,
"search.exclude": {

View File

@ -70,7 +70,8 @@ order: 68
"type": "page",
"data": {
"step": 1,
"status": "error"
"status": "error",
"secondTitle": "Second"
},
"body": [
{
@ -84,7 +85,7 @@ order: 68
"description": "this is description"
},
{
"title": "Second"
"title": "${secondTitle}"
},
{
"title": "Last"
@ -236,13 +237,14 @@ order: 68
### step
| 属性名 | 类型 | 默认值 | 说明 |
| --------- | -------- | ------ | --------------------------------------- |
| title | `string` | `-` | 标题 |
| subTitle | `string` | `-` | 子标题 |
| icon | `string` | | icon 名,支持 fontawesome v4 或使用 url |
| value | `string` | | value |
| className | `string` | `-` | 自定义类名 |
| 属性名 | 类型 | 默认值 | 说明 |
| ----------- | --------------- | ------ | --------------------------------------- |
| title | `string \| tpl` | | 标题 |
| subTitle | `string \| tpl` | | 子标题 |
| description | `string \| tpl` | | 详细描述 |
| icon | `string` | | icon 名,支持 fontawesome v4 或使用 url |
| value | `string` | | value |
| className | `string` | | 自定义类名 |
### StepStatus

View File

@ -2,165 +2,198 @@ import React from 'react';
import {Renderer, RendererProps} from '../factory';
import {BaseSchema} from '../Schema';
import {Icon} from '../components/icons';
import {RemoteOptionsProps, withRemoteConfig} from '../components/WithRemoteConfig';
import {
RemoteOptionsProps,
withRemoteConfig
} from '../components/WithRemoteConfig';
import {resolveVariable} from '../utils/tpl-builtin';
import {filter} from '../utils/tpl';
enum StepStatus {
wait = 'wait',
process = 'process',
finish = 'finish',
error = 'error'
};
wait = 'wait',
process = 'process',
finish = 'finish',
error = 'error'
}
export type StepSchema = {
/**
*
*/
title: string;
/**
*
*/
title: string;
/**
*
*/
subTitle?: string;
/**
*
*/
subTitle?: string;
/**
*
*/
icon?: string;
/**
*
*/
icon?: string;
value?: string | number;
value?: string | number;
/**
*
*/
description?: string;
/**
*
*/
description?: string;
} & Omit<BaseSchema, 'type'>;
export interface StepsSchema extends BaseSchema {
/**
* Steps
*/
type: 'steps';
/**
* Steps
*/
type: 'steps';
/**
*
*/
steps?: Array<StepSchema>;
/**
*
*/
steps?: Array<StepSchema>;
/**
* API
*/
source?: string;
/**
* API
*/
source?: string;
/**
*
*/
value?: number | string;
/**
*
*/
value?: number | string;
/**
*
*/
name?: string;
/**
*
*/
name?: string;
status?: StepStatus | {
[propName: string]: StepStatus;
}
status?:
| StepStatus
| {
[propName: string]: StepStatus;
};
/**
*
*/
mode?: 'horizontal' | 'vertical';
/**
*
*/
mode?: 'horizontal' | 'vertical';
}
export interface StepsProps extends RendererProps, Omit<StepsSchema, 'className'> {}
export interface StepsProps
extends RendererProps,
Omit<StepsSchema, 'className'> {}
export function Steps(props: StepsProps) {
const {className, classnames: cx, steps, value = 0, status, data, source, config} = props;
const stepsRow = resolveVariable(source, data) as Array<StepSchema> || config || steps || [];
const resolveValue = typeof value === 'string' && isNaN(+value)
? resolveVariable(value, data) as string || +value : +value;
const valueIndex = stepsRow.findIndex(item => item.value && item.value === resolveValue);
const currentValue = valueIndex !== -1 ? valueIndex : resolveValue;
const FINISH_ICON = 'check';
const ERROR_ICON = 'close';
const {
className,
classnames: cx,
steps,
value = 0,
status,
data,
source,
config
} = props;
const stepsRow =
(resolveVariable(source, data) as Array<StepSchema>) ||
config ||
steps ||
[];
const resolveValue =
typeof value === 'string' && isNaN(+value)
? (resolveVariable(value, data) as string) || +value
: +value;
const valueIndex = stepsRow.findIndex(
item => item.value && item.value === resolveValue
);
const currentValue = valueIndex !== -1 ? valueIndex : resolveValue;
const FINISH_ICON = 'check';
const ERROR_ICON = 'close';
function getStepStatus(step: StepSchema, i: number): {stepStatus: StepStatus, icon?: string} {
let stepStatus = StepStatus.wait;
let icon = step.icon;
function getStepStatus(
step: StepSchema,
i: number
): {stepStatus: StepStatus; icon?: string} {
let stepStatus = StepStatus.wait;
let icon = step.icon;
if (i < currentValue) {
stepStatus = StepStatus.finish;
!icon && (icon = FINISH_ICON);
}
else if (i === currentValue) {
stepStatus = StepStatus.process;
}
if (i < currentValue) {
stepStatus = StepStatus.finish;
!icon && (icon = FINISH_ICON);
} else if (i === currentValue) {
stepStatus = StepStatus.process;
}
if (typeof status === 'string') {
if (i === currentValue) {
const resolveStatus = resolveVariable(status, data);
stepStatus = resolveStatus || status || StepStatus.process;
stepStatus === StepStatus.error && !icon && (icon = ERROR_ICON);
}
}
else if (typeof status === 'object') {
const key = step.value;
key && status[key] && (stepStatus = status[key]);
}
return {
stepStatus,
icon
}
}
if (typeof status === 'string') {
if (i === currentValue) {
const resolveStatus = resolveVariable(status, data);
stepStatus = resolveStatus || status || StepStatus.process;
stepStatus === StepStatus.error && !icon && (icon = ERROR_ICON);
}
} else if (typeof status === 'object') {
const key = step.value;
key && status[key] && (stepStatus = status[key]);
}
return (
<ul className={cx('Steps', className)}>
{stepsRow.map((step, i) => {
const {stepStatus, icon} = getStepStatus(step, i);
return {
stepStatus,
icon
};
}
return (
<li key={i} className={cx('StepsItem', `is-${stepStatus}`, step.className)}>
<div className={cx('StepsItem-container')}>
<div className={cx('StepsItem-containerIcon')}>
<span className={cx('StepsItem-icon')}>
{
icon ? <Icon icon={icon} className="icon" /> : (i + 1)
}
</span>
</div>
<div className={cx('StepsItem-containerWrapper')}>
<div className={cx('StepsItem-body')}>
<div className={cx('StepsItem-title', i < currentValue && 'is-success')}>
<span>{step.title}</span>
<span className={cx('StepsItem-subTitle')}>{step.subTitle || step.value}</span>
</div>
<div className={cx('StepsItem-description')}>{step.description}</div>
</div>
</div>
</div>
</li>
)
})}
</ul>
)
return (
<ul className={cx('Steps', className)}>
{stepsRow.map((step, i) => {
const {stepStatus, icon} = getStepStatus(step, i);
return (
<li
key={i}
className={cx('StepsItem', `is-${stepStatus}`, step.className)}
>
<div className={cx('StepsItem-container')}>
<div className={cx('StepsItem-containerIcon')}>
<span className={cx('StepsItem-icon')}>
{icon ? <Icon icon={icon} className="icon" /> : i + 1}
</span>
</div>
<div className={cx('StepsItem-containerWrapper')}>
<div className={cx('StepsItem-body')}>
<div
className={cx(
'StepsItem-title',
i < currentValue && 'is-success'
)}
>
<span>{filter(step.title, data)}</span>
<span className={cx('StepsItem-subTitle')}>
{filter(step.subTitle || step.value, data)}
</span>
</div>
<div className={cx('StepsItem-description')}>
{filter(step.description, data)}
</div>
</div>
</div>
</div>
</li>
);
})}
</ul>
);
}
const StepsWithRemoteConfig = withRemoteConfig({
adaptor: data => data.steps || data
adaptor: data => data.steps || data
})(
class extends React.Component<
RemoteOptionsProps & React.ComponentProps<typeof Steps>
> {
render() {
const {config, ...rest} = this.props;
return (
<Steps config={config} {...rest} />
);
}
}
)
class extends React.Component<
RemoteOptionsProps & React.ComponentProps<typeof Steps>
> {
render() {
const {config, ...rest} = this.props;
return <Steps config={config} {...rest} />;
}
}
);
@Renderer({
test: /(^|\/)steps$/,

View File

@ -16,7 +16,7 @@ export function registerTplEnginer(name: string, enginer: Enginer) {
}
export function filter(
tpl?: string,
tpl?: any,
data: object = {},
...rest: Array<any>
): string {