mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-03 20:39:07 +08:00
feat: export-excel 支持自定义导出列 (#3783)
This commit is contained in:
parent
efaab83f65
commit
ad54c29450
@ -1858,6 +1858,61 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
|
||||
}
|
||||
```
|
||||
|
||||
#### 自定义导出列
|
||||
|
||||
> 1.8.0 及以上版本
|
||||
|
||||
除了简单隐藏某些列,还可以通过 `exportColumns` 完全控制导出列,比如新增某些列,它的配置项和 `columns` 一致
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"headerToolbar": [{
|
||||
"type": "export-excel",
|
||||
"label": "导出 Excel",
|
||||
"exportColumns": [
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
},
|
||||
{
|
||||
"type": "tpl",
|
||||
"label": "tpl",
|
||||
"tpl": "${browser}"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "ID"
|
||||
},
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Rendering engine"
|
||||
},
|
||||
{
|
||||
"name": "browser",
|
||||
"label": "Browser"
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"label": "Platform(s)"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Engine version"
|
||||
},
|
||||
{
|
||||
"name": "grade",
|
||||
"label": "CSS grade"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 通过 api 导出 Excel
|
||||
|
||||
> 1.1.6 以上版本支持
|
||||
|
@ -26,7 +26,7 @@ export interface PullRefreshProps {
|
||||
loadingDuration?: number;
|
||||
}
|
||||
|
||||
type statusText = 'normal' | 'pulling' | 'loosing' |'success' | 'loading';
|
||||
type statusText = 'normal' | 'pulling' | 'loosing' | 'success' | 'loading';
|
||||
|
||||
export interface PullRefreshState {
|
||||
status: statusText;
|
||||
@ -41,7 +41,6 @@ const defaultProps = {
|
||||
const defaultHeaderHeight = 28;
|
||||
|
||||
const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
|
||||
const {
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
@ -59,11 +58,11 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
|
||||
const touch = useTouch();
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
if (props.loading === false) {
|
||||
loadSuccess();
|
||||
}
|
||||
},[props.loading]);
|
||||
}, [props.loading]);
|
||||
|
||||
const [state, updateState] = useSetState({
|
||||
status: 'normal',
|
||||
@ -71,8 +70,12 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
} as PullRefreshState);
|
||||
|
||||
const isTouchable = () => {
|
||||
return !props.disabled && state.status !== 'loading' && state.status !== 'success';
|
||||
}
|
||||
return (
|
||||
!props.disabled &&
|
||||
state.status !== 'loading' &&
|
||||
state.status !== 'success'
|
||||
);
|
||||
};
|
||||
|
||||
const ease = (distance: number) => {
|
||||
const pullDistance = defaultHeaderHeight;
|
||||
@ -118,13 +121,13 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
};
|
||||
|
||||
const onTouchStart = (event: any) => {
|
||||
event.stopPropagation()
|
||||
event.stopPropagation();
|
||||
|
||||
if (isTouchable() && state.offsetY === 0) {
|
||||
touch.start(event);
|
||||
updateState({});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onTouchMove = (event: any) => {
|
||||
event.stopPropagation();
|
||||
@ -137,7 +140,7 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const onTouchEnd = (event: any) => {
|
||||
event.stopPropagation();
|
||||
@ -154,7 +157,7 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
setStatus(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const transformStyle = {
|
||||
transform: `translate3d(0, ${state.offsetY}px, 0)`,
|
||||
@ -166,7 +169,7 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
return '';
|
||||
}
|
||||
return props[`${status}Text`] || refreshText[`${status}Text`];
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -178,7 +181,9 @@ const PullRefresh = forwardRef<{}, PullRefreshProps>((props, ref) => {
|
||||
>
|
||||
<div className={cx('PullRefresh-wrap')} style={transformStyle}>
|
||||
<div className={cx('PullRefresh-header')}>
|
||||
{state.status === 'loading' && <Icon icon="loading-outline" className="icon loading-icon" />}
|
||||
{state.status === 'loading' && (
|
||||
<Icon icon="loading-outline" className="icon loading-icon" />
|
||||
)}
|
||||
{getStatusText(state.status)}
|
||||
</div>
|
||||
{children}
|
||||
|
@ -228,7 +228,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||
'change',
|
||||
createObject(data, {
|
||||
value: newValue,
|
||||
options,
|
||||
options
|
||||
})
|
||||
);
|
||||
if (rendererEvent?.prevented) {
|
||||
@ -410,7 +410,9 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||
onFocus={(e: any) => this.dispatchEvent('focus', e)}
|
||||
onAdd={() => this.dispatchEvent('add')}
|
||||
onEdit={(item: any) => this.dispatchEvent('edit', {value: item})}
|
||||
onDelete={(item: any) => this.dispatchEvent('delete', {value: item})}
|
||||
onDelete={(item: any) =>
|
||||
this.dispatchEvent('delete', {value: item})
|
||||
}
|
||||
loading={loading}
|
||||
noResultsText={noResultsText}
|
||||
renderMenu={menuTpl ? this.renderMenu : undefined}
|
||||
|
@ -744,28 +744,30 @@ export default class Page extends React.Component<PageProps> {
|
||||
|
||||
const styleVar = buildStyle(style, data);
|
||||
|
||||
const pageContent = <div className={cx('Page-content')}>
|
||||
<div className={cx('Page-main')}>
|
||||
{this.renderHeader()}
|
||||
<div className={cx(`Page-body`, bodyClassName)}>
|
||||
<Spinner size="lg" overlay key="info" show={store.loading} />
|
||||
const pageContent = (
|
||||
<div className={cx('Page-content')}>
|
||||
<div className={cx('Page-main')}>
|
||||
{this.renderHeader()}
|
||||
<div className={cx(`Page-body`, bodyClassName)}>
|
||||
<Spinner size="lg" overlay key="info" show={store.loading} />
|
||||
|
||||
{store.error && showErrorMsg !== false ? (
|
||||
<Alert
|
||||
level="danger"
|
||||
showCloseButton
|
||||
onClose={store.clearMessage}
|
||||
>
|
||||
{store.msg}
|
||||
</Alert>
|
||||
) : null}
|
||||
{store.error && showErrorMsg !== false ? (
|
||||
<Alert
|
||||
level="danger"
|
||||
showCloseButton
|
||||
onClose={store.clearMessage}
|
||||
>
|
||||
{store.msg}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{(Array.isArray(regions) ? ~regions.indexOf('body') : body)
|
||||
? render('body', body || '', subProps)
|
||||
: null}
|
||||
{(Array.isArray(regions) ? ~regions.indexOf('body') : body)
|
||||
? render('body', body || '', subProps)
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -799,15 +801,17 @@ export default class Page extends React.Component<PageProps> {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{pullRefresh && !pullRefresh.disabled
|
||||
? <PullRefresh
|
||||
{...pullRefresh}
|
||||
translate={__}
|
||||
onRefresh={this.handleRefresh}
|
||||
>
|
||||
{pageContent}
|
||||
</PullRefresh>
|
||||
: pageContent}
|
||||
{pullRefresh && !pullRefresh.disabled ? (
|
||||
<PullRefresh
|
||||
{...pullRefresh}
|
||||
translate={__}
|
||||
onRefresh={this.handleRefresh}
|
||||
>
|
||||
{pageContent}
|
||||
</PullRefresh>
|
||||
) : (
|
||||
pageContent
|
||||
)}
|
||||
|
||||
{render(
|
||||
'dialog',
|
||||
|
@ -370,6 +370,7 @@ export interface TableProps extends RendererProps {
|
||||
type ExportExcelToolbar = SchemaNode & {
|
||||
api?: SchemaApi;
|
||||
columns?: string[];
|
||||
exportColumns?: any[];
|
||||
filename?: string;
|
||||
};
|
||||
|
||||
@ -2261,11 +2262,12 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classPrefix: ns,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
columns,
|
||||
data,
|
||||
render
|
||||
} = this.props;
|
||||
|
||||
let columns = store.filteredColumns || [];
|
||||
|
||||
if (!columns) {
|
||||
return null;
|
||||
}
|
||||
@ -2318,19 +2320,28 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
});
|
||||
worksheet.views = [{state: 'frozen', xSplit: 0, ySplit: 1}];
|
||||
|
||||
let exportColumns = toolbar.columns;
|
||||
if (isPureVariable(exportColumns)) {
|
||||
exportColumns = resolveVariableAndFilter(
|
||||
exportColumns,
|
||||
let exportColumnNames = toolbar.columns;
|
||||
|
||||
if (isPureVariable(exportColumnNames)) {
|
||||
exportColumnNames = resolveVariableAndFilter(
|
||||
exportColumnNames,
|
||||
data,
|
||||
'| raw'
|
||||
);
|
||||
}
|
||||
|
||||
const filteredColumns = exportColumns
|
||||
// 自定义导出列配置
|
||||
if (toolbar.exportColumns && Array.isArray(toolbar.exportColumns)) {
|
||||
columns = toolbar.exportColumns;
|
||||
}
|
||||
|
||||
const filteredColumns = exportColumnNames
|
||||
? columns.filter(column => {
|
||||
const filterColumnsNames = exportColumns!;
|
||||
if (filterColumnsNames.indexOf(column.name) !== -1) {
|
||||
const filterColumnsNames = exportColumnNames!;
|
||||
if (
|
||||
column.name &&
|
||||
filterColumnsNames.indexOf(column.name) !== -1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -253,7 +253,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
}
|
||||
})
|
||||
.then(value => {
|
||||
this.handleInitEvent(store.data)
|
||||
this.handleInitEvent(store.data);
|
||||
|
||||
const state = {
|
||||
currentStep:
|
||||
@ -340,14 +340,19 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
async dispatchEvent(action: string, value?: object) {
|
||||
const {dispatchEvent, data} = this.props;
|
||||
|
||||
const rendererEvent = await dispatchEvent(action, createObject(data, value ? value : {}));
|
||||
const rendererEvent = await dispatchEvent(
|
||||
action,
|
||||
createObject(data, value ? value : {})
|
||||
);
|
||||
|
||||
return rendererEvent?.prevented ?? false;
|
||||
}
|
||||
|
||||
async handleInitEvent(data: any) {
|
||||
const {onInit} = this.props;
|
||||
(await this.dispatchEvent('inited', {formData: data})) && onInit && onInit(data);
|
||||
(await this.dispatchEvent('inited', {formData: data})) &&
|
||||
onInit &&
|
||||
onInit(data);
|
||||
}
|
||||
|
||||
@autobind
|
||||
@ -383,8 +388,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
index = Math.max(Math.min(steps.length, index), 1);
|
||||
|
||||
if (index != this.state.currentStep) {
|
||||
if (await this.dispatchEvent('stepChange', {step: index, formData: this.props.store.data})) {
|
||||
return
|
||||
if (
|
||||
await this.dispatchEvent('stepChange', {
|
||||
step: index,
|
||||
formData: this.props.store.data
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@ -537,7 +547,11 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
) {
|
||||
const {onAction, store, env, steps} = this.props;
|
||||
|
||||
if (action.actionType === 'next' || action.type === 'submit' || action.actionType === 'step-submit') {
|
||||
if (
|
||||
action.actionType === 'next' ||
|
||||
action.type === 'submit' ||
|
||||
action.actionType === 'step-submit'
|
||||
) {
|
||||
this.form.doAction(
|
||||
{
|
||||
...action,
|
||||
@ -593,11 +607,15 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
} else if (action.actionType === 'goto-step') {
|
||||
const targetStep = (data as any).step;
|
||||
|
||||
if (targetStep !== undefined && targetStep <= steps.length && targetStep >= 0) {
|
||||
if (
|
||||
targetStep !== undefined &&
|
||||
targetStep <= steps.length &&
|
||||
targetStep >= 0
|
||||
) {
|
||||
this.gotoStep((data as any).step);
|
||||
}
|
||||
} else if (action.actionType === 'submit') {
|
||||
this.finalSubmit()
|
||||
this.finalSubmit();
|
||||
} else if (onAction) {
|
||||
onAction(e, action, data, throwErrors, delegate || this.context);
|
||||
}
|
||||
@ -630,14 +648,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
async handleChange(values: object) {
|
||||
const {store} = this.props;
|
||||
|
||||
const previous = store.data;
|
||||
const previous = store.data;
|
||||
const final = {...previous, ...values};
|
||||
|
||||
if (await this.dispatchEvent('change', {formData: final})) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
store.updateData(values);
|
||||
}
|
||||
|
||||
@ -685,7 +702,6 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
const step = steps[this.state.currentStep - 1];
|
||||
store.updateData(values);
|
||||
|
||||
|
||||
// 最后一步
|
||||
if (target) {
|
||||
this.submitToTarget(target, store.data);
|
||||
@ -766,7 +782,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
return value;
|
||||
})
|
||||
.catch(error => {
|
||||
this.dispatchEvent('submitFail', {error})
|
||||
this.dispatchEvent('submitFail', {error});
|
||||
store.markSaving(false);
|
||||
console.error(error);
|
||||
});
|
||||
@ -779,12 +795,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
// 接管里面 form 的提交,不能直接让 form 提交,因为 wizard 自己需要知道进度。
|
||||
@autobind
|
||||
async handleSubmit(values: object, action: Action) {
|
||||
const {
|
||||
store,
|
||||
steps,
|
||||
finishedField
|
||||
} = this.props;
|
||||
|
||||
const {store, steps, finishedField} = this.props;
|
||||
|
||||
if (this.state.currentStep < steps.length) {
|
||||
const step = steps[this.state.currentStep - 1];
|
||||
|
@ -127,14 +127,14 @@ export function calculatePosition(
|
||||
atX === 'left'
|
||||
? childOffset.left
|
||||
: atX === 'right'
|
||||
? childOffset.left + childOffset.width
|
||||
: childOffset.left + childOffset.width / 2;
|
||||
? childOffset.left + childOffset.width
|
||||
: childOffset.left + childOffset.width / 2;
|
||||
positionTop =
|
||||
atY === 'top'
|
||||
? childOffset.top
|
||||
: atY === 'bottom'
|
||||
? childOffset.top + childOffset.height
|
||||
: childOffset.top + childOffset.height / 2;
|
||||
? childOffset.top + childOffset.height
|
||||
: childOffset.top + childOffset.height / 2;
|
||||
|
||||
positionLeft -=
|
||||
myX === 'left' ? 0 : myX === 'right' ? overlayWidth : overlayWidth / 2;
|
||||
@ -142,8 +142,8 @@ export function calculatePosition(
|
||||
myY === 'top'
|
||||
? 0
|
||||
: myY === 'bottom'
|
||||
? overlayHeight
|
||||
: overlayHeight / 2;
|
||||
? overlayHeight
|
||||
: overlayHeight / 2;
|
||||
|
||||
// 如果还有其他可选项,则做位置判断,是否在可视区域,不完全在则继续看其他定位情况。
|
||||
if (tests.length) {
|
||||
|
Loading…
Reference in New Issue
Block a user