feat: 核心组件相关改动 (#6025)

* feat: 核心组件相关改动

* table2 列搜索自定义schema问题修复

* table2 column 不将title传递下去

* 不能传递 valueField 给table,否则table中的表单项valueField 会被覆盖
This commit is contained in:
sansiro 2023-01-13 16:10:19 +08:00 committed by GitHub
parent 522d9c06c8
commit 45dc93c07f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 131 additions and 54 deletions

View File

@ -219,6 +219,8 @@ export interface ApiObject extends BaseApiObject {
query?: PlainObject;
adaptor?: (payload: object, response: fetcherResult, api: ApiObject) => any;
requestAdaptor?: (api: ApiObject) => ApiObject;
/** 是否过滤为空字符串的 query 参数 */
filterEmptyQuery?: boolean;
}
export type ApiString = string;
export type Api = ApiString | ApiObject;

View File

@ -129,6 +129,19 @@ export function buildApi(
);
};
// 是否过滤空字符串 query
const queryStringify = (query: any) =>
qsstringify(
query,
(api as ApiObject)?.filterEmptyQuery
? {
filter: (key: string, value: any) => {
return value === '' ? undefined : value;
}
}
: undefined
);
if (~idx) {
const hashIdx = url.indexOf('#');
const params = qsparse(
@ -146,10 +159,13 @@ export function buildApi(
});
const left = replaceExpression(url.substring(0, idx), 'raw', '');
// 追加
Object.assign(params, api.query);
api.url =
left +
(~left.indexOf('?') ? '&' : '?') +
qsstringify(
queryStringify(
(api.query = dataMapping(params, data, undefined, api.convertKeyToPath))
) +
(~hashIdx && hashIdx > idx
@ -178,13 +194,22 @@ export function buildApi(
api.body = api.data = data;
}
// 给 query 做数据映射
if (api.query) {
api.query = dataMapping(api.query, data, undefined, api.convertKeyToPath);
}
// get 类请求,把 data 附带到 url 上。
if (api.method === 'get' || api.method === 'jsonp' || api.method === 'js') {
if (
!api.data &&
((!~raw.indexOf('$') && autoAppend) || api.forceAppendDataToQuery)
) {
api.query = api.data = data;
api.data = data;
api.query = {
...api.query,
...data
};
} else if (
api.attachDataToQuery === false &&
api.data &&
@ -194,12 +219,13 @@ export function buildApi(
if (~idx) {
let params = (api.query = {
...qsparse(api.url.substring(idx + 1)),
...api.query,
...data
});
api.url = api.url.substring(0, idx) + '?' + qsstringify(params);
api.url = api.url.substring(0, idx) + '?' + queryStringify(params);
} else {
api.query = data;
const query = qsstringify(data);
api.query = {...api.query, ...data};
const query = queryStringify(data);
if (query) {
api.url = `${api.url}?${query}`;
}
@ -211,12 +237,13 @@ export function buildApi(
if (~idx) {
let params = (api.query = {
...qsparse(api.url.substring(idx + 1)),
...api.query,
...api.data
});
api.url = api.url.substring(0, idx) + '?' + qsstringify(params);
api.url = api.url.substring(0, idx) + '?' + queryStringify(params);
} else {
api.query = api.data;
const query = qsstringify(api.data);
api.query = {...api.query, ...api.data};
const query = queryStringify(api.query);
if (query) {
api.url = `${api.url}?${query}`;
}
@ -224,6 +251,22 @@ export function buildApi(
delete api.data;
}
}
// 非 get 类请求也可以携带参数到 url只要 query 有值
else if (api.method) {
const idx = api.url.indexOf('?');
if (~idx) {
let params = (api.query = {
...qsparse(api.url.substring(idx + 1)),
...api.query
});
api.url = api.url.substring(0, idx) + '?' + queryStringify(params);
} else {
const query = queryStringify(api.query);
if (query) {
api.url = `${api.url}?${query}`;
}
}
}
if (api.graphql) {
if (api.method === 'get') {

View File

@ -5,7 +5,7 @@ export function getVariable(
key: string | undefined,
canAccessSuper: boolean = true
): any {
if (!data || !key) {
if (!data || !key || typeof data !== 'object') {
return undefined;
} else if (canAccessSuper ? key in data : data.hasOwnProperty(key)) {
return data[key];

View File

@ -21,7 +21,9 @@ export function normalizeOptions(
const option = {
label: item,
value: item
// 添加 option 的 value 根据 valueField 来
// 否则某些情况下多余字段会有影响
[valueField]: item
};
share.values.push(option.value);
@ -41,10 +43,10 @@ export function normalizeOptions(
const option = {
label: item,
value: item
[valueField]: item
};
share.values.push(option.value);
share.values.push(option[valueField]);
share.options.push(option);
return option;
@ -64,7 +66,7 @@ export function normalizeOptions(
const option = {
...item,
value
[valueField]: value
};
if (typeof option.children !== 'undefined') {
@ -85,7 +87,7 @@ export function normalizeOptions(
const option = {
label: (options as {[propName: string]: string})[key] as string,
value: key
[valueField]: key
};
share.values.push(option.value);

View File

@ -18,6 +18,7 @@ import {ColumnProps} from './index';
export interface Props extends ThemeProps, LocaleProps {
column: ColumnProps;
onSort?: Function;
active?: boolean;
classnames: ClassNamesFn;
}
@ -36,31 +37,46 @@ export class HeadCellSort extends React.Component<Props, State> {
};
}
componentDidUpdate(prevProps: Readonly<Props>): void {
const props = this.props;
// 失效后重置,同时只能有一列在排序
if (
props?.active !== undefined &&
!props?.active &&
props.active !== prevProps?.active
) {
this.setState({orderBy: '', order: ''});
}
}
render() {
const {column, onSort, classnames: cx} = this.props;
const {active, column, onSort, classnames: cx} = this.props;
return (
<span
className={cx('TableCell-sortBtn')}
onClick={async () => {
let sortPayload = {};
let sortPayload: State = {
orderBy: '',
order: ''
};
if (column.name === this.state.orderBy) {
if (this.state.order === 'descend') {
if (this.state.order === 'desc') {
// 降序改为取消
sortPayload = {orderBy: '', order: 'ascend'};
sortPayload = {orderBy: '', order: ''};
} else {
// 升序之后降序
sortPayload = {order: 'descend'};
sortPayload = {orderBy: column.name, order: 'desc'};
}
} else {
// 默认先升序
sortPayload = {orderBy: column.name, order: 'ascend'};
sortPayload = {orderBy: column.name, order: 'asc'};
}
if (onSort) {
const prevented = await onSort({
orderBy: this.state.orderBy,
order: this.state.order
orderBy: sortPayload.orderBy,
order: sortPayload.order
});
if (prevented) {
return;
@ -73,9 +89,7 @@ export class HeadCellSort extends React.Component<Props, State> {
<i
className={cx(
'TableCell-sortBtn--down',
this.state.orderBy === column.name && this.state.order === 'descend'
? 'is-active'
: ''
active && this.state.order === 'desc' ? 'is-active' : ''
)}
>
<Icon icon="sort-desc" className="icon" />
@ -83,9 +97,7 @@ export class HeadCellSort extends React.Component<Props, State> {
<i
className={cx(
'TableCell-sortBtn--up',
this.state.orderBy === column.name && this.state.order === 'ascend'
? 'is-active'
: ''
active && this.state.order === 'asc' ? 'is-active' : ''
)}
>
<Icon icon="sort-asc" className="icon" />
@ -93,7 +105,7 @@ export class HeadCellSort extends React.Component<Props, State> {
<i
className={cx(
'TableCell-sortBtn--default',
this.state.orderBy === column.name ? '' : 'is-active'
active ? '' : 'is-active'
)}
>
<Icon icon="sort-default" className="icon" />

View File

@ -42,7 +42,7 @@ export interface ColumnProps {
fixed?: boolean | string;
width?: number | string;
sorter?: (a: any, b: any) => number | boolean; // 设置为true时执行onSort否则执行前端排序
sortOrder?: string; // 升序ascend、降序descend
sortOrder?: string; // 升序asc、降序desc
filters?: Array<any>; // 筛选数据源,配置了数据源才展示
filterMode?: string; // menu/tree 默认menu 先只支持menu
filterMultiple?: boolean; // 是否支持多选
@ -173,6 +173,7 @@ export interface TableState {
record: any;
target: HTMLTableRowElement;
} | null;
sort?: SortProps;
}
function getMaxLevelThRowSpan(columns: Array<ColumnProps>) {
@ -1048,24 +1049,32 @@ export class Table extends React.PureComponent<TableProps, TableState> {
sort = (
<HeadCellSort
column={item}
onSort={
onSort
? onSort
: (payload: SortProps) => {
if (typeof item.sorter === 'function') {
if (payload.orderBy) {
const sortList = [...this.state.dataSource];
this.setState({
dataSource: sortList.sort(
item.sorter as (a: any, b: any) => number
)
});
} else {
this.setState({dataSource: [...dataSource]});
}
}
}
active={
this.state.sort?.orderBy &&
this.state.sort.orderBy === item?.name
}
onSort={(payload: SortProps) => {
this.setState({
sort: payload
});
if (onSort) {
onSort(payload);
} else {
if (typeof item.sorter === 'function') {
if (payload.orderBy) {
const sortList = [...this.state.dataSource];
this.setState({
dataSource: sortList.sort(
item.sorter as (a: any, b: any) => number
)
});
} else {
this.setState({dataSource: [...dataSource]});
}
}
}
}}
></HeadCellSort>
);
}

View File

@ -35,7 +35,7 @@ import {ListSchema} from './List';
import {TableSchema2} from './Table2';
import {isPureVariable, resolveVariableAndFilter} from 'amis-core';
import {SchemaCollection} from '../Schema';
import {upperFirst} from 'lodash';
import upperFirst from 'lodash/upperFirst';
export type CRUDRendererEvent = 'search';
@ -425,9 +425,12 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
replaceQuery?: boolean;
}) {
const {store, syncLocation, env, pageField, perPageField} = this.props;
let {query, resetQuery, replaceQuery} = data;
let {query, resetQuery, replaceQuery} = data || {};
query = syncLocation ? qsparse(qsstringify(query, undefined, true)) : query;
query =
syncLocation && query
? qsparse(qsstringify(query, undefined, true))
: query;
store.updateQuery(
resetQuery ? this.props.store.pristineQuery : query,
@ -917,7 +920,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
doAction(action: Action, data: object, throwErrors: boolean = false) {
if (
action.actionType &&
~['stopAutoRefresh', 'reload', 'search', 'startAutoRefresh'].includes(
['stopAutoRefresh', 'reload', 'search', 'startAutoRefresh'].includes(
action.actionType
)
) {
@ -1159,14 +1162,14 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
: undefined,
keepItemSelectionOnPageChange,
maxKeepItemSelectionLength,
valueField: valueField || primaryField,
// valueField: valueField || primaryField,
primaryField: primaryField,
items: store.data.items,
query: store.query,
orderBy: store.query.orderBy,
orderDir: store.query.orderDir,
popOverContainer,
onSave: this.handleSave,
onSave: this.handleSave.bind(this),
onSaveOrder: this.handleSaveOrder,
onSearch: this.handleTableQuery,
onSort: this.handleTableQuery,

View File

@ -73,6 +73,8 @@ export class HeadCellSearchDropDown extends React.Component<
? searchable.controls.concat()
: undefined
};
} else if (searchable?.type === 'form') {
schema = searchable;
} else {
schema = {
title: '',
@ -128,6 +130,8 @@ export class HeadCellSearchDropDown extends React.Component<
...schema,
type: 'form',
wrapperComponent: 'div',
wrapWithPanel: true,
title: false,
actions: [
{
type: 'button',

View File

@ -579,7 +579,9 @@ export default class Table2 extends React.Component<Table2Props, object> {
// table2的拆开了 就不需要再设置div的width了
// 否则加上padding 就超出单元格的区域了
// children属性在schema里是一个关键字 在渲染器schema中 自定义的children没有用 去掉
const {width, children, ...rest} = schema;
// title 需要去掉,否则部分组件会将其渲染出来
const {width, children, title, ...rest} = schema;
return render(
'cell-field',
{