mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
chore: 同步table性能优化 (#8414)
* chore: 优化 Table 渲染性能
* chore: 优化 Table 渲染性能
* chore: 修复性能优化后 列固定宽度不刷新问题 (#8040)
* chore: 调整table nested 样式 (#8077)
* chore: 优化 Table 渲染单元格性能
* 剔除无关属性下发
* 优化 table 宽度初始化
* fix: 修复性能优化后在需要用到父级数据的时候没有及时更新的问题 Close: #8188 (#8193)
* Revert "fix: table 添加 toggled: false 的列不参与持久化存储 (#5236)" (#8156)
This reverts commit e7c909b277
.
* fix: 修复编辑器表格在有快速编辑配置时点选不上的问题 Close: #8208 (#8210)
* fix: 修复嵌套模式当列隐藏时不显示展开按钮问题 Close: #8301
* fix: 修复CRUD固定表头不生效问题 (#8332)
* fix: 修复固定表头不生效
* 修复低版本浏览器固定表头不生效问题
---------
Co-authored-by: huangxj29 <huangxuejuan@xgd.com>
* fix: 修复table columns、tableLayout属性变化realWidth未更新
* fix:锁定@types/codemirror修复单测问题
* fix: 删除多余单测
---------
Co-authored-by: 2betop <2betop.cn@gmail.com>
Co-authored-by: wanglinfang <wanglinfang@baidu.com>
Co-authored-by: huangxj29 <2824346520@qq.com>
Co-authored-by: huangxj29 <huangxuejuan@xgd.com>
This commit is contained in:
parent
144cf613e0
commit
ec21a1c0f3
@ -334,10 +334,13 @@ order: 67
|
||||
|
||||
可以配置数字,用于设置列宽像素,例如下面例子我们给`Rendering engine`列宽设置为`100px`。
|
||||
|
||||
> 如果希望精准的控制列宽,请设置表格的 `tableLayout` 为 `fixed` 模式,同时为了让表格标题不换行,标题文字的长短会影响列的最小宽度
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"api": "/api/mock2/sample?waitSeconds=1",
|
||||
"tableLayout": "fixed",
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
@ -346,7 +349,7 @@ order: 67
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Rendering engine",
|
||||
"width": 100
|
||||
"width": 150
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1845,6 +1848,8 @@ popOver 的其它配置请参考 [popover](./popover)
|
||||
| resizable | `boolean` | `true` | 列宽度是否支持调整 | |
|
||||
| selectable | `boolean` | `false` | 支持勾选 | |
|
||||
| multiple | `boolean` | `false` | 勾选 icon 是否为多选样式`checkbox`, 默认为`radio` | |
|
||||
| lazyRenderAfter | `number` | `100` | 用来控制从第几行开始懒渲染行,用来渲染大表格时有用 | |
|
||||
| tableLayout | `auto` \| `fixed` | `auto` | 当配置为 fixed 时,内容将不会撑开表格,自动换行 | |
|
||||
|
||||
### 列配置属性表
|
||||
|
||||
|
@ -15,7 +15,7 @@ export default {
|
||||
sortable: true,
|
||||
type: 'text',
|
||||
toggled: true,
|
||||
width: 100
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
name: 'engine',
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = [
|
||||
const list = [
|
||||
{
|
||||
engine: 'Trident',
|
||||
browser: 'Internet Explorer 4.0',
|
||||
@ -1202,8 +1202,21 @@ module.exports = [
|
||||
version: '-',
|
||||
grade: 'U'
|
||||
}
|
||||
].map(function (item, index) {
|
||||
return Object.assign({}, item, {
|
||||
id: index + 1
|
||||
];
|
||||
|
||||
// 多来点测试数据
|
||||
module.exports = list
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.concat(list)
|
||||
.map(function (item, index) {
|
||||
return Object.assign({}, item, {
|
||||
id: index + 1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -57,3 +57,22 @@ export const createMockMediaMatcher =
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Mock IntersectionObserver
|
||||
class IntersectionObserver {
|
||||
observe = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
}
|
||||
|
||||
Object.defineProperty(window, 'IntersectionObserver', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: IntersectionObserver
|
||||
});
|
||||
|
||||
Object.defineProperty(global, 'IntersectionObserver', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: IntersectionObserver
|
||||
});
|
||||
|
@ -46,7 +46,7 @@
|
||||
"esm"
|
||||
],
|
||||
"dependencies": {
|
||||
"amis-formula": "^3.4.0",
|
||||
"amis-formula": "*",
|
||||
"classnames": "2.3.2",
|
||||
"file-saver": "^2.0.2",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
@ -58,8 +58,8 @@
|
||||
"moment": "^2.19.4",
|
||||
"papaparse": "^5.3.0",
|
||||
"qs": "6.9.7",
|
||||
"react-intersection-observer": "9.5.2",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-visibility-sensor": "5.1.1",
|
||||
"tslib": "^2.3.1",
|
||||
"uncontrollable": "7.2.1"
|
||||
},
|
||||
|
@ -440,14 +440,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
exprProps = {};
|
||||
}
|
||||
|
||||
// style 支持公式
|
||||
if (schema.style) {
|
||||
// schema.style是readonly属性
|
||||
schema = {...schema, style: buildStyle(schema.style, detectData)};
|
||||
}
|
||||
|
||||
const isClassComponent = Component.prototype?.isReactComponent;
|
||||
const $schema = {...schema, ...exprProps};
|
||||
let props = {
|
||||
...theme.getRendererConfig(renderer.name),
|
||||
...restSchema,
|
||||
@ -459,7 +452,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
defaultActiveKey: defaultActiveKey,
|
||||
propKey: propKey,
|
||||
$path: $path,
|
||||
$schema: $schema,
|
||||
$schema: schema,
|
||||
ref: this.refFn,
|
||||
render: this.renderChild,
|
||||
rootStore,
|
||||
@ -468,6 +461,11 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
mobileUI: schema.useMobileUI === false ? false : rest.mobileUI
|
||||
};
|
||||
|
||||
// style 支持公式
|
||||
if (schema.style) {
|
||||
(props as any).style = buildStyle(schema.style, detectData);
|
||||
}
|
||||
|
||||
if (disable !== undefined) {
|
||||
(props as any).disabled = disable;
|
||||
}
|
||||
@ -478,7 +476,7 @@ export class SchemaRenderer extends React.Component<SchemaRendererProps, any> {
|
||||
|
||||
// 自动解析变量模式,主要是方便直接引入第三方组件库,无需为了支持变量封装一层
|
||||
if (renderer.autoVar) {
|
||||
for (const key of Object.keys($schema)) {
|
||||
for (const key of Object.keys(schema)) {
|
||||
if (typeof props[key] === 'string') {
|
||||
props[key] = resolveVariableAndFilter(
|
||||
props[key],
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
import {InView} from 'react-intersection-observer';
|
||||
|
||||
export interface LazyComponentProps {
|
||||
component?: React.ElementType;
|
||||
@ -13,7 +13,6 @@ export interface LazyComponentProps {
|
||||
placeholder?: React.ReactNode;
|
||||
unMountOnHidden?: boolean;
|
||||
childProps?: object;
|
||||
visiblilityProps?: object;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -56,7 +55,7 @@ export default class LazyComponent extends React.Component<
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
handleVisibleChange(visible: boolean) {
|
||||
handleVisibleChange(visible: boolean, entry?: any) {
|
||||
this.setState({
|
||||
visible: visible
|
||||
});
|
||||
@ -91,7 +90,6 @@ export default class LazyComponent extends React.Component<
|
||||
placeholder,
|
||||
unMountOnHidden,
|
||||
childProps,
|
||||
visiblilityProps,
|
||||
partialVisibility,
|
||||
children,
|
||||
...rest
|
||||
@ -102,33 +100,42 @@ export default class LazyComponent extends React.Component<
|
||||
// 需要监听从可见到不可见。
|
||||
if (unMountOnHidden) {
|
||||
return (
|
||||
<VisibilitySensor
|
||||
{...visiblilityProps}
|
||||
partialVisibility={partialVisibility}
|
||||
<InView
|
||||
onChange={this.handleVisibleChange}
|
||||
threshold={partialVisibility ? 0 : 1}
|
||||
>
|
||||
<div className="visibility-sensor">
|
||||
{Component && visible ? (
|
||||
<Component {...rest} {...childProps} />
|
||||
) : children && visible ? (
|
||||
children
|
||||
) : (
|
||||
placeholder
|
||||
)}
|
||||
</div>
|
||||
</VisibilitySensor>
|
||||
{({ref}) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={`visibility-sensor ${visible ? 'in' : ''}`}
|
||||
>
|
||||
{Component && visible ? (
|
||||
<Component {...rest} {...childProps} />
|
||||
) : children && visible ? (
|
||||
children
|
||||
) : (
|
||||
placeholder
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</InView>
|
||||
);
|
||||
}
|
||||
|
||||
if (!visible) {
|
||||
return (
|
||||
<VisibilitySensor
|
||||
{...visiblilityProps}
|
||||
partialVisibility={partialVisibility}
|
||||
<InView
|
||||
onChange={this.handleVisibleChange}
|
||||
threshold={partialVisibility ? 0 : 1}
|
||||
>
|
||||
<div className="visibility-sensor">{placeholder}</div>
|
||||
</VisibilitySensor>
|
||||
{({ref}) => (
|
||||
<div ref={ref} className="visibility-sensor">
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
</InView>
|
||||
);
|
||||
} else if (Component) {
|
||||
// 只监听不可见到可见,一旦可见了,就销毁检查。
|
||||
|
@ -3,7 +3,7 @@ import {IFormStore, IFormItemStore} from '../store/form';
|
||||
import debouce from 'lodash/debounce';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {RendererProps, Renderer} from '../factory';
|
||||
import {RendererProps, Renderer, getRendererByName} from '../factory';
|
||||
import {ComboStore, IComboStore, IUniqueGroup} from '../store/combo';
|
||||
import {
|
||||
anyChanged,
|
||||
@ -32,7 +32,7 @@ import {FormBaseControl, FormItemWrap} from './Item';
|
||||
import {Api} from '../types';
|
||||
import {TableStore} from '../store/table';
|
||||
import pick from 'lodash/pick';
|
||||
import {callStrFunction} from '../utils';
|
||||
import {callStrFunction, changedEffect} from '../utils';
|
||||
|
||||
export interface ControlOutterProps extends RendererProps {
|
||||
formStore?: IFormStore;
|
||||
@ -121,6 +121,8 @@ export function wrapControl<
|
||||
onChange,
|
||||
data,
|
||||
inputGroupControl,
|
||||
colIndex,
|
||||
rowIndex,
|
||||
$schema: {
|
||||
name,
|
||||
id,
|
||||
@ -154,17 +156,14 @@ export function wrapControl<
|
||||
this.setPrinstineValue = this.setPrinstineValue.bind(this);
|
||||
this.controlRef = this.controlRef.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.validate = this.validate.bind(this);
|
||||
this.flushChange = this.flushChange.bind(this);
|
||||
|
||||
if (!name) {
|
||||
// 一般情况下这些表单项都是需要 name 的,提示一下
|
||||
if (
|
||||
typeof type === 'string' &&
|
||||
(type.startsWith('input-') ||
|
||||
type.endsWith('select') ||
|
||||
type === 'switch' ||
|
||||
type === 'textarea' ||
|
||||
type === 'radios') &&
|
||||
type !== 'input-group'
|
||||
getRendererByName(type)?.isFormItem
|
||||
) {
|
||||
console.warn('name is required', this.props.$schema);
|
||||
}
|
||||
@ -178,7 +177,9 @@ export function wrapControl<
|
||||
path: this.props.$path,
|
||||
storeType: FormItemStore.name,
|
||||
parentId: store?.id,
|
||||
name
|
||||
name,
|
||||
colIndex: colIndex !== undefined ? colIndex : undefined,
|
||||
rowIndex: rowIndex !== undefined ? rowIndex : undefined
|
||||
}) as IFormItemStore;
|
||||
this.model = model;
|
||||
// @issue 打算干掉这个
|
||||
@ -226,6 +227,7 @@ export function wrapControl<
|
||||
if (propValue !== undefined && propValue !== null) {
|
||||
// 同步 value: 优先使用 props 中的 value
|
||||
model.changeTmpValue(propValue, 'controlled');
|
||||
model.setIsControlled(true);
|
||||
} else {
|
||||
const isExp = isExpression(value);
|
||||
|
||||
@ -332,12 +334,10 @@ export function wrapControl<
|
||||
|
||||
componentDidUpdate(prevProps: OuterProps) {
|
||||
const props = this.props;
|
||||
const form = props.formStore;
|
||||
const model = this.model;
|
||||
|
||||
if (
|
||||
model &&
|
||||
anyChanged(
|
||||
model &&
|
||||
changedEffect(
|
||||
[
|
||||
'id',
|
||||
'validations',
|
||||
@ -362,34 +362,17 @@ export function wrapControl<
|
||||
'extraName'
|
||||
],
|
||||
prevProps.$schema,
|
||||
props.$schema
|
||||
)
|
||||
) {
|
||||
model.config({
|
||||
required: props.$schema.required,
|
||||
id: props.$schema.id,
|
||||
unique: props.$schema.unique,
|
||||
value: props.$schema.value,
|
||||
isValueSchemaExp: isExpression(props.$schema.value),
|
||||
rules: props.$schema.validations,
|
||||
multiple: props.$schema.multiple,
|
||||
delimiter: props.$schema.delimiter,
|
||||
valueField: props.$schema.valueField,
|
||||
labelField: props.$schema.labelField,
|
||||
joinValues: props.$schema.joinValues,
|
||||
extractValue: props.$schema.extractValue,
|
||||
messages: props.$schema.validationErrors,
|
||||
selectFirst: props.$schema.selectFirst,
|
||||
autoFill: props.$schema.autoFill,
|
||||
clearValueOnHidden: props.$schema.clearValueOnHidden,
|
||||
validateApi: props.$schema.validateApi,
|
||||
minLength: props.$schema.minLength,
|
||||
maxLength: props.$schema.maxLength,
|
||||
label: props.$schema.label,
|
||||
inputGroupControl: props?.inputGroupControl,
|
||||
extraName: props.$schema.extraName
|
||||
});
|
||||
}
|
||||
props.$schema,
|
||||
changes => {
|
||||
model.config({
|
||||
...changes,
|
||||
|
||||
// todo 优化后面两个
|
||||
isValueSchemaExp: isExpression(props.$schema.value),
|
||||
inputGroupControl: props?.inputGroupControl
|
||||
} as any);
|
||||
}
|
||||
);
|
||||
|
||||
// 此处需要同时考虑 defaultValue 和 value
|
||||
if (model && typeof props.value !== 'undefined') {
|
||||
@ -605,13 +588,16 @@ export function wrapControl<
|
||||
result = [await this.model.validate(data)];
|
||||
}
|
||||
|
||||
if (result && result.length) {
|
||||
if (result.indexOf(false) > -1) {
|
||||
formItemDispatchEvent('formItemValidateError', data);
|
||||
} else {
|
||||
formItemDispatchEvent('formItemValidateSucc', data);
|
||||
}
|
||||
}
|
||||
const valid = !result.some(item => item === false);
|
||||
formItemDispatchEvent?.(
|
||||
valid ? 'formItemValidateSucc' : 'formItemValidateError',
|
||||
data
|
||||
);
|
||||
return valid;
|
||||
}
|
||||
|
||||
flushChange() {
|
||||
this.lazyEmitChange.flush();
|
||||
}
|
||||
|
||||
handleChange(
|
||||
@ -862,6 +848,8 @@ export function wrapControl<
|
||||
getValue: this.getValue,
|
||||
prinstine: model ? model.prinstine : undefined,
|
||||
setPrinstineValue: this.setPrinstineValue,
|
||||
onValidate: this.validate,
|
||||
onFlushChange: this.flushChange,
|
||||
// !没了这个, tree 里的 options 渲染会出问题
|
||||
_filteredOptions: this.model?.filteredOptions
|
||||
};
|
||||
|
@ -117,26 +117,27 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
|
||||
replace: boolean = false
|
||||
) {
|
||||
const originQuery = self.query;
|
||||
self.query = replace
|
||||
const query: any = replace
|
||||
? {
|
||||
...values
|
||||
}
|
||||
: {
|
||||
...self.query,
|
||||
...originQuery,
|
||||
...values
|
||||
};
|
||||
|
||||
if (self.query[pageField || 'page']) {
|
||||
self.page = parseInt(self.query[pageField || 'page'], 10);
|
||||
}
|
||||
if (isObjectShallowModified(originQuery, query, false)) {
|
||||
if (query[pageField || 'page']) {
|
||||
self.page = parseInt(query[pageField || 'page'], 10);
|
||||
}
|
||||
|
||||
if (self.query[perPageField || 'perPage']) {
|
||||
self.perPage = parseInt(self.query[perPageField || 'perPage'], 10);
|
||||
}
|
||||
if (query[perPageField || 'perPage']) {
|
||||
self.perPage = parseInt(query[perPageField || 'perPage'], 10);
|
||||
}
|
||||
|
||||
updater &&
|
||||
isObjectShallowModified(originQuery, self.query, false) &&
|
||||
setTimeout(updater.bind(null, `?${qsstringify(self.query)}`), 4);
|
||||
self.query = query;
|
||||
updater && setTimeout(updater.bind(null, `?${qsstringify(query)}`), 4);
|
||||
}
|
||||
}
|
||||
|
||||
const fetchInitData: (
|
||||
|
@ -49,7 +49,7 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
while (pool.length) {
|
||||
const current = pool.shift()!;
|
||||
|
||||
if (current.storeType === 'FormItemStore') {
|
||||
if (current.storeType === 'FormItemStore' && !current.isControlled) {
|
||||
formItems.push(current);
|
||||
} else if (
|
||||
!['ComboStore', 'TableStore', 'FormStore'].includes(current.storeType)
|
||||
|
@ -67,6 +67,7 @@ const getSelectedOptionsCache: any = {
|
||||
export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
.props({
|
||||
isFocused: false,
|
||||
isControlled: false, // 是否是受控表单项,通常是用在别的组件里面
|
||||
type: '',
|
||||
label: '',
|
||||
unique: false,
|
||||
@ -107,7 +108,9 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
resetValue: types.optional(types.frozen(), ''),
|
||||
validateOnChange: false,
|
||||
/** 当前表单项所属的InputGroup父元素, 用于收集InputGroup的子元素 */
|
||||
inputGroupControl: types.optional(types.frozen(), {})
|
||||
inputGroupControl: types.optional(types.frozen(), {}),
|
||||
colIndex: types.frozen(),
|
||||
rowIndex: types.frozen()
|
||||
})
|
||||
.views(self => {
|
||||
function getForm(): any {
|
||||
@ -358,28 +361,35 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
inputGroupControl?.name != null &&
|
||||
(self.inputGroupControl = inputGroupControl);
|
||||
|
||||
rules = {
|
||||
...rules,
|
||||
isRequired: self.required || rules?.isRequired
|
||||
};
|
||||
if (
|
||||
typeof rules !== 'undefined' ||
|
||||
self.required ||
|
||||
typeof minLength === 'number' ||
|
||||
typeof maxLength === 'number'
|
||||
) {
|
||||
rules = {
|
||||
...rules,
|
||||
isRequired: self.required || rules?.isRequired
|
||||
};
|
||||
|
||||
// todo 这个弄个配置由渲染器自己来决定
|
||||
// 暂时先这样
|
||||
if (~['input-text', 'textarea'].indexOf(self.type)) {
|
||||
if (typeof minLength === 'number') {
|
||||
rules.minLength = minLength;
|
||||
// todo 这个弄个配置由渲染器自己来决定
|
||||
// 暂时先这样
|
||||
if (~['input-text', 'textarea'].indexOf(self.type)) {
|
||||
if (typeof minLength === 'number') {
|
||||
rules.minLength = minLength;
|
||||
}
|
||||
|
||||
if (typeof maxLength === 'number') {
|
||||
rules.maxLength = maxLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof maxLength === 'number') {
|
||||
rules.maxLength = maxLength;
|
||||
if (isObjectShallowModified(rules, self.rules)) {
|
||||
self.rules = rules;
|
||||
clearError('builtin');
|
||||
self.validated = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectShallowModified(rules, self.rules)) {
|
||||
self.rules = rules;
|
||||
clearError('builtin');
|
||||
self.validated = false;
|
||||
}
|
||||
}
|
||||
|
||||
function focus() {
|
||||
@ -1336,6 +1346,10 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
}
|
||||
}
|
||||
|
||||
function setIsControlled(value: any) {
|
||||
self.isControlled = !!value;
|
||||
}
|
||||
|
||||
return {
|
||||
focus,
|
||||
blur,
|
||||
@ -1361,7 +1375,8 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
changeEmitedValue,
|
||||
addSubFormItem,
|
||||
removeSubFormItem,
|
||||
loadAutoUpdateData
|
||||
loadAutoUpdateData,
|
||||
setIsControlled
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
import {Instance, types} from 'mobx-state-tree';
|
||||
import {parseQuery} from '../utils/helper';
|
||||
import {ServiceStore} from './service';
|
||||
import {createObjectFromChain, extractObjectChain} from '../utils';
|
||||
import {
|
||||
createObjectFromChain,
|
||||
extractObjectChain,
|
||||
isObjectShallowModified
|
||||
} from '../utils';
|
||||
|
||||
export const RootStore = ServiceStore.named('RootStore')
|
||||
.props({
|
||||
@ -42,7 +46,10 @@ export const RootStore = ServiceStore.named('RootStore')
|
||||
self.runtimeErrorStack = errorStack;
|
||||
},
|
||||
updateLocation(location?: any, parseFn?: Function) {
|
||||
self.query = parseFn ? parseFn(location) : parseQuery(location);
|
||||
const query = parseFn ? parseFn(location) : parseQuery(location);
|
||||
if (isObjectShallowModified(query, self.query, false)) {
|
||||
self.query = query;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -51,7 +51,6 @@ export const Column = types
|
||||
toggable: true,
|
||||
expandable: false,
|
||||
checkdisable: false,
|
||||
isPrimary: false,
|
||||
searchable: types.maybe(types.frozen()),
|
||||
enableSearch: true,
|
||||
sortable: false,
|
||||
@ -61,11 +60,19 @@ export const Column = types
|
||||
rawIndex: 0,
|
||||
width: 0,
|
||||
minWidth: 0,
|
||||
realWidth: 0,
|
||||
breakpoint: types.optional(types.frozen(), undefined),
|
||||
pristine: types.optional(types.frozen(), undefined),
|
||||
remark: types.optional(types.frozen(), undefined),
|
||||
className: types.union(types.string, types.frozen())
|
||||
})
|
||||
.views(self => ({
|
||||
get isPrimary() {
|
||||
const table = getParent(self, 2) as any;
|
||||
|
||||
return table.filteredColumns[0]?.id === self.id;
|
||||
}
|
||||
}))
|
||||
.actions(self => ({
|
||||
toggleToggle(min = 1) {
|
||||
self.toggled = !self.toggled;
|
||||
@ -89,11 +96,16 @@ export const Column = types
|
||||
table.persistSaveToggledColumns();
|
||||
},
|
||||
|
||||
setWidth(value: number, minWidth?: number) {
|
||||
setMinWidth(value: number) {
|
||||
self.minWidth = value;
|
||||
},
|
||||
|
||||
setWidth(value: number) {
|
||||
self.width = value;
|
||||
if (typeof minWidth === 'number') {
|
||||
self.minWidth = minWidth;
|
||||
}
|
||||
},
|
||||
|
||||
setRealWidth(value: number) {
|
||||
self.realWidth = value;
|
||||
}
|
||||
}));
|
||||
|
||||
@ -111,6 +123,7 @@ export const Row = types
|
||||
rowSpans: types.frozen({} as any),
|
||||
index: types.number,
|
||||
newIndex: types.number,
|
||||
nth: 0,
|
||||
path: '', // 行数据的位置
|
||||
expandable: false,
|
||||
checkdisable: false,
|
||||
@ -119,7 +132,9 @@ export const Row = types
|
||||
types.array(types.late((): IAnyModelType => Row)),
|
||||
[]
|
||||
),
|
||||
depth: types.number // 当前children位于第几层,便于使用getParent获取最顶层TableStore
|
||||
depth: types.number, // 当前children位于第几层,便于使用getParent获取最顶层TableStore
|
||||
appeared: true,
|
||||
lazyRender: false
|
||||
})
|
||||
.views(self => ({
|
||||
get checked(): boolean {
|
||||
@ -253,6 +268,12 @@ export const Row = types
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
get indentStyle() {
|
||||
return {
|
||||
paddingLeft: `calc(${self.depth - 1} * var(--Table-tree-indent))`
|
||||
};
|
||||
}
|
||||
}))
|
||||
.actions(self => ({
|
||||
@ -321,6 +342,10 @@ export const Row = types
|
||||
index++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
markAppeared(value: any) {
|
||||
value && (self.appeared = !!value);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -344,6 +369,8 @@ export const TableStore = iRendererStore
|
||||
),
|
||||
'asc'
|
||||
),
|
||||
loading: false,
|
||||
canAccessSuperData: false,
|
||||
draggable: false,
|
||||
dragging: false,
|
||||
selectable: false,
|
||||
@ -365,7 +392,9 @@ export const TableStore = iRendererStore
|
||||
keepItemSelectionOnPageChange: false,
|
||||
// 导出 Excel 按钮的 loading 状态
|
||||
exportExcelLoading: false,
|
||||
searchFormExpanded: false // 用来控制搜索框是否展开了,那个自动根据 searchable 生成的表单 autoGenerateFilter
|
||||
searchFormExpanded: false, // 用来控制搜索框是否展开了,那个自动根据 searchable 生成的表单 autoGenerateFilter
|
||||
lazyRenderAfter: 100,
|
||||
tableLayout: 'auto'
|
||||
})
|
||||
.views(self => {
|
||||
function getColumnsExceptBuiltinTypes() {
|
||||
@ -417,7 +446,7 @@ export const TableStore = iRendererStore
|
||||
: item.type === '__dragme'
|
||||
? self.dragging
|
||||
: item.type === '__expandme'
|
||||
? (getFootableColumns().length || self.isNested) && !self.dragging
|
||||
? getFootableColumns().length && !self.dragging
|
||||
: (item.toggled || !item.toggable) &&
|
||||
(!self.footable ||
|
||||
!item.breakpoint ||
|
||||
@ -688,10 +717,12 @@ export const TableStore = iRendererStore
|
||||
return getUnSelectedRows();
|
||||
},
|
||||
|
||||
get falttenedRows() {
|
||||
return flattenTree<IRow>(self.rows);
|
||||
},
|
||||
|
||||
get checkableRows() {
|
||||
return flattenTree<IRow>(self.rows).filter(
|
||||
(item: IRow) => item.checkable
|
||||
);
|
||||
return this.falttenedRows.filter((item: IRow) => item.checkable);
|
||||
},
|
||||
|
||||
get expandableRows() {
|
||||
@ -760,7 +791,7 @@ export const TableStore = iRendererStore
|
||||
},
|
||||
|
||||
get columnWidthReady() {
|
||||
return getFilteredColumns().every(column => column.width);
|
||||
return getFilteredColumns().every(column => column.realWidth);
|
||||
},
|
||||
|
||||
getStickyStyles(column: IColumn, columns: Array<IColumn>) {
|
||||
@ -789,21 +820,25 @@ export const TableStore = iRendererStore
|
||||
stickyClassName += ' is-sticky-last-left';
|
||||
}
|
||||
|
||||
let left = 0;
|
||||
let left = [];
|
||||
while (index >= 0) {
|
||||
const col = columns[index];
|
||||
if (
|
||||
(col && col.fixed === 'left') ||
|
||||
autoFixLeftColumns.includes(col.type)
|
||||
) {
|
||||
left += col.width;
|
||||
left.push(`var(--Table-column-${col.index}-width)`);
|
||||
}
|
||||
index--;
|
||||
}
|
||||
style.left = left;
|
||||
style.left = left.length
|
||||
? left.length === 1
|
||||
? left[0]
|
||||
: `calc(${left.join(' + ')})`
|
||||
: 0;
|
||||
} else if (column.fixed === 'right') {
|
||||
stickyClassName = 'is-sticky is-sticky-right';
|
||||
let right = 0;
|
||||
let right = [];
|
||||
let index = columns.indexOf(column) + 1;
|
||||
|
||||
if (columns.slice(0, index - 1).every(col => col.fixed !== 'right')) {
|
||||
@ -814,13 +849,32 @@ export const TableStore = iRendererStore
|
||||
while (index < len) {
|
||||
const col = columns[index];
|
||||
if (col && col.fixed === 'right') {
|
||||
right += col.width;
|
||||
right.push(`var(--Table-column-${col.index}-width)`);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
style.right = right;
|
||||
style.right = right.length
|
||||
? right.length === 1
|
||||
? right[0]
|
||||
: `calc(${right.join(' + ')})`
|
||||
: 0;
|
||||
}
|
||||
return [style, stickyClassName];
|
||||
},
|
||||
|
||||
get items() {
|
||||
return self.rows.concat();
|
||||
},
|
||||
|
||||
buildStyles(style: any) {
|
||||
style = {...style};
|
||||
|
||||
getFilteredColumns().forEach(column => {
|
||||
style[`--Table-column-${column.index}-width`] =
|
||||
column.realWidth + 'px';
|
||||
});
|
||||
|
||||
return style;
|
||||
}
|
||||
};
|
||||
})
|
||||
@ -831,47 +885,68 @@ export const TableStore = iRendererStore
|
||||
tableRef = ref;
|
||||
}
|
||||
|
||||
function update(config: Partial<STableStore>) {
|
||||
config.primaryField !== void 0 &&
|
||||
(self.primaryField = config.primaryField);
|
||||
config.selectable !== void 0 && (self.selectable = config.selectable);
|
||||
config.columnsTogglable !== void 0 &&
|
||||
(self.columnsTogglable = config.columnsTogglable);
|
||||
config.draggable !== void 0 && (self.draggable = config.draggable);
|
||||
function getTable() {
|
||||
return tableRef;
|
||||
}
|
||||
|
||||
if (typeof config.orderBy === 'string') {
|
||||
function update(config: Partial<STableStore>) {
|
||||
config.primaryField !== undefined &&
|
||||
(self.primaryField = config.primaryField);
|
||||
config.selectable !== undefined && (self.selectable = config.selectable);
|
||||
config.columnsTogglable !== undefined &&
|
||||
(self.columnsTogglable = config.columnsTogglable);
|
||||
config.draggable !== undefined && (self.draggable = config.draggable);
|
||||
|
||||
if (
|
||||
typeof config.orderBy === 'string' ||
|
||||
typeof config.orderDir === 'string'
|
||||
) {
|
||||
setOrderByInfo(
|
||||
config.orderBy,
|
||||
config.orderDir === 'desc' ? 'desc' : 'asc'
|
||||
config.orderBy ?? self.orderBy,
|
||||
config.orderDir !== undefined
|
||||
? config.orderDir === 'desc'
|
||||
? 'desc'
|
||||
: 'asc'
|
||||
: self.orderDir
|
||||
);
|
||||
}
|
||||
|
||||
config.multiple !== void 0 && (self.multiple = config.multiple);
|
||||
config.footable !== void 0 && (self.footable = config.footable);
|
||||
config.expandConfig !== void 0 &&
|
||||
config.multiple !== undefined && (self.multiple = config.multiple);
|
||||
config.footable !== undefined && (self.footable = config.footable);
|
||||
config.expandConfig !== undefined &&
|
||||
(self.expandConfig = config.expandConfig);
|
||||
config.itemCheckableOn !== void 0 &&
|
||||
config.itemCheckableOn !== undefined &&
|
||||
(self.itemCheckableOn = config.itemCheckableOn);
|
||||
config.itemDraggableOn !== void 0 &&
|
||||
config.itemDraggableOn !== undefined &&
|
||||
(self.itemDraggableOn = config.itemDraggableOn);
|
||||
config.hideCheckToggler !== void 0 &&
|
||||
config.hideCheckToggler !== undefined &&
|
||||
(self.hideCheckToggler = !!config.hideCheckToggler);
|
||||
|
||||
config.combineNum !== void 0 &&
|
||||
config.combineNum !== undefined &&
|
||||
(self.combineNum = parseInt(config.combineNum as any, 10) || 0);
|
||||
config.combineFromIndex !== void 0 &&
|
||||
config.combineFromIndex !== undefined &&
|
||||
(self.combineFromIndex =
|
||||
parseInt(config.combineFromIndex as any, 10) || 0);
|
||||
|
||||
config.maxKeepItemSelectionLength !== void 0 &&
|
||||
config.maxKeepItemSelectionLength !== undefined &&
|
||||
(self.maxKeepItemSelectionLength = config.maxKeepItemSelectionLength);
|
||||
config.keepItemSelectionOnPageChange !== void 0 &&
|
||||
config.keepItemSelectionOnPageChange !== undefined &&
|
||||
(self.keepItemSelectionOnPageChange =
|
||||
config.keepItemSelectionOnPageChange);
|
||||
|
||||
config.exportExcelLoading !== undefined &&
|
||||
(self.exportExcelLoading = config.exportExcelLoading);
|
||||
|
||||
config.loading !== undefined && (self.loading = config.loading);
|
||||
config.canAccessSuperData !== undefined &&
|
||||
(self.canAccessSuperData = !!config.canAccessSuperData);
|
||||
|
||||
typeof config.lazyRenderAfter === 'number' &&
|
||||
(self.lazyRenderAfter = config.lazyRenderAfter);
|
||||
|
||||
typeof config.tableLayout === 'string' &&
|
||||
(self.tableLayout = config.tableLayout);
|
||||
|
||||
if (config.columns && Array.isArray(config.columns)) {
|
||||
let columns: Array<SColumn> = config.columns
|
||||
.filter(column => column)
|
||||
@ -933,21 +1008,21 @@ export const TableStore = iRendererStore
|
||||
const originColumns = self.columns.concat();
|
||||
columns = columns.map((item, index) => {
|
||||
const origin = item.id
|
||||
? originColumns.find(column => column.id === item.id)
|
||||
? originColumns.find(column => column.pristine.id === item.id)
|
||||
: originColumns[index];
|
||||
|
||||
return {
|
||||
...item,
|
||||
id: guid(),
|
||||
id: origin?.id || guid(),
|
||||
index,
|
||||
width: origin?.width || 0,
|
||||
minWidth: origin?.minWidth || 0,
|
||||
realWidth: origin?.realWidth || 0,
|
||||
rawIndex: index - PARTITION_INDEX,
|
||||
type: item.type || 'plain',
|
||||
pristine: item.pristine || item,
|
||||
toggled: item.toggled !== false,
|
||||
breakpoint: item.breakpoint,
|
||||
isPrimary: index === PARTITION_INDEX,
|
||||
/** 提前映射变量,方便后续view中使用 */
|
||||
label: isPureVariable(item.label)
|
||||
? resolveVariableAndFilter(item.label, self.data)
|
||||
@ -959,31 +1034,69 @@ export const TableStore = iRendererStore
|
||||
}
|
||||
}
|
||||
|
||||
function syncTableWidth() {
|
||||
function initTableWidth() {
|
||||
const table = tableRef;
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
const tableWidth = table.parentElement!.offsetWidth;
|
||||
const thead = table.querySelector(':scope>thead')!;
|
||||
const tbody = table.querySelector(':scope>tbody');
|
||||
let tbody: HTMLElement | null = null;
|
||||
const htmls: Array<string> = [];
|
||||
const isFixed = self.tableLayout === 'fixed';
|
||||
const someSettedWidth = self.columns.some(
|
||||
column => column.pristine.width
|
||||
);
|
||||
|
||||
const minWidths: {
|
||||
[propName: string]: number;
|
||||
} = {};
|
||||
|
||||
// fixed 模式需要参考 auto 获得列最小宽度
|
||||
if (isFixed) {
|
||||
tbody = table.querySelector(':scope>tbody');
|
||||
htmls.push(
|
||||
`<table style="table-layout:auto!important;width:0!important;min-width:0!important;" class="${table.className}">${thead.outerHTML}</table>`
|
||||
);
|
||||
}
|
||||
|
||||
if (someSettedWidth || isFixed) {
|
||||
htmls.push(
|
||||
`<table style="table-layout:auto!important;min-width:${tableWidth}px!important;width:${tableWidth}px!important;" class="${table.className.replace(
|
||||
'is-layout-fixed',
|
||||
''
|
||||
)}">${thead.outerHTML}${
|
||||
tbody ? `<tbody>${tbody.innerHTML}</tbody>` : ''
|
||||
}</table>`
|
||||
);
|
||||
}
|
||||
|
||||
if (!htmls.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'amis-scope'; // jssdk 里面 css 会在这一层
|
||||
div.style.cssText += `visibility: hidden!important;`;
|
||||
div.innerHTML =
|
||||
`<table style="table-layout:auto!important;width:0!important;min-width:0!important;" class="${table.className}">${thead.outerHTML}</table>` +
|
||||
`<table style="table-layout:auto!important;min-width:${tableWidth}px!important;width:${tableWidth}px!important;" class="${table.className.replace(
|
||||
'is-layout-fixed',
|
||||
''
|
||||
)}">${thead.outerHTML}${
|
||||
tbody ? `<tbody>${tbody.innerHTML}</tbody>` : ''
|
||||
}</table>`;
|
||||
const ths1: Array<HTMLTableCellElement> = [].slice.call(
|
||||
div.querySelectorAll(':scope>table:first-child>thead>tr>th[data-index]')
|
||||
);
|
||||
const ths2: Array<HTMLTableCellElement> = [].slice.call(
|
||||
div.querySelectorAll(':scope>table:last-child>thead>tr>th[data-index]')
|
||||
);
|
||||
div.innerHTML = htmls.join('');
|
||||
let ths1: Array<HTMLTableCellElement> = [];
|
||||
let ths2: Array<HTMLTableCellElement> = [];
|
||||
|
||||
if (isFixed) {
|
||||
ths1 = [].slice.call(
|
||||
div.querySelectorAll(
|
||||
':scope>table:first-child>thead>tr>th[data-index]'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (someSettedWidth || isFixed) {
|
||||
ths2 = [].slice.call(
|
||||
div.querySelectorAll(
|
||||
':scope>table:last-child>thead>tr>th[data-index]'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
ths1.forEach(th => {
|
||||
th.style.cssText += 'width: 0';
|
||||
@ -997,36 +1110,50 @@ export const TableStore = iRendererStore
|
||||
? `width: ${column.pristine.width}px;`
|
||||
: column.pristine.width
|
||||
? `width: ${column.pristine.width};`
|
||||
: ''
|
||||
: '' // todo 可能需要让修改过列宽的保持相应宽度,目前这样相当于重置了
|
||||
}`;
|
||||
});
|
||||
|
||||
document.body.appendChild(div);
|
||||
const minWidths: {
|
||||
[propName: string]: number;
|
||||
} = {};
|
||||
|
||||
ths1.forEach((th: HTMLTableCellElement) => {
|
||||
minWidths[th.getAttribute('data-index')!] = th.clientWidth;
|
||||
const index = parseInt(th.getAttribute('data-index')!, 10);
|
||||
minWidths[index] = th.clientWidth;
|
||||
const column = self.columns[index];
|
||||
column.setMinWidth(minWidths[index]);
|
||||
});
|
||||
|
||||
ths2.forEach((col: HTMLElement) => {
|
||||
const index = parseInt(col.getAttribute('data-index')!, 10);
|
||||
const column = self.columns[index];
|
||||
column.setWidth(
|
||||
Math.max(
|
||||
typeof column.pristine.width === 'number'
|
||||
? column.pristine.width
|
||||
: col.clientWidth - 2,
|
||||
minWidths[index]
|
||||
),
|
||||
minWidths[index]
|
||||
);
|
||||
if (column.pristine.width || isFixed) {
|
||||
column.setWidth(
|
||||
Math.max(
|
||||
typeof column.pristine.width === 'number'
|
||||
? column.pristine.width
|
||||
: col.clientWidth,
|
||||
minWidths[index] || 0
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
document.body.removeChild(div);
|
||||
}
|
||||
|
||||
function invalidTableColumnWidth() {
|
||||
self.columns.forEach(column => column.setWidth(0));
|
||||
function syncTableWidth() {
|
||||
const table = tableRef;
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
const cols = [].slice.call(
|
||||
table.querySelectorAll(':scope>thead>tr>th[data-index]')
|
||||
);
|
||||
cols.forEach((col: HTMLElement) => {
|
||||
const index = parseInt(col.getAttribute('data-index')!, 10);
|
||||
const column = self.columns[index];
|
||||
column.setRealWidth(col.offsetWidth);
|
||||
});
|
||||
}
|
||||
|
||||
function combineCell(arr: Array<SRow>, keys: Array<string>): Array<SRow> {
|
||||
@ -1112,7 +1239,8 @@ export const TableStore = iRendererStore
|
||||
depth: number,
|
||||
pindex: number,
|
||||
parentId: string,
|
||||
path: string = ''
|
||||
path: string = '',
|
||||
nThRef: {index: number}
|
||||
): any {
|
||||
depth += 1;
|
||||
return children.map((item, index) => {
|
||||
@ -1131,6 +1259,7 @@ export const TableStore = iRendererStore
|
||||
path: `${path}${index}`,
|
||||
depth: depth,
|
||||
index: index,
|
||||
nth: nThRef.index++,
|
||||
newIndex: index,
|
||||
pristine: item,
|
||||
data: item,
|
||||
@ -1142,7 +1271,8 @@ export const TableStore = iRendererStore
|
||||
depth,
|
||||
index,
|
||||
id,
|
||||
`${path}${index}.`
|
||||
`${path}${index}.`,
|
||||
nThRef
|
||||
)
|
||||
: [],
|
||||
expandable: !!(
|
||||
@ -1164,6 +1294,7 @@ export const TableStore = iRendererStore
|
||||
/* 避免输入内容为非数组挂掉 */
|
||||
rows = !Array.isArray(rows) ? [] : rows;
|
||||
|
||||
const nThRef = {index: 0};
|
||||
let arr: Array<SRow> = rows.map((item, index) => {
|
||||
if (!isObject(item)) {
|
||||
item = {
|
||||
@ -1180,6 +1311,7 @@ export const TableStore = iRendererStore
|
||||
key: String(`${index}-1-${index}`),
|
||||
depth: 1, // 最大父节点默认为第一层,逐层叠加
|
||||
index: index,
|
||||
nth: nThRef.index++,
|
||||
newIndex: index,
|
||||
pristine: item,
|
||||
path: `${index}`,
|
||||
@ -1187,7 +1319,7 @@ export const TableStore = iRendererStore
|
||||
rowSpans: {},
|
||||
children:
|
||||
item && Array.isArray(item.children)
|
||||
? initChildren(item.children, 1, index, id, `${index}.`)
|
||||
? initChildren(item.children, 1, index, id, `${index}.`, nThRef)
|
||||
: [],
|
||||
expandable: !!(
|
||||
(item && Array.isArray(item.children) && item.children.length) ||
|
||||
@ -1208,6 +1340,21 @@ export const TableStore = iRendererStore
|
||||
replaceRow(arr, reUseRow);
|
||||
self.isNested = self.rows.some(item => item.children.length);
|
||||
|
||||
// 前 20 个直接渲染,后面的按需渲染
|
||||
if (
|
||||
self.lazyRenderAfter &&
|
||||
self.falttenedRows.length > self.lazyRenderAfter
|
||||
) {
|
||||
for (
|
||||
let i = self.lazyRenderAfter, len = self.falttenedRows.length;
|
||||
i < len;
|
||||
i++
|
||||
) {
|
||||
self.falttenedRows[i].appeared = false;
|
||||
self.falttenedRows[i].lazyRender = true;
|
||||
}
|
||||
}
|
||||
|
||||
const expand = self.footable && self.footable.expand;
|
||||
if (
|
||||
expand === 'first' ||
|
||||
@ -1539,10 +1686,8 @@ export const TableStore = iRendererStore
|
||||
localStorage.setItem(
|
||||
key,
|
||||
JSON.stringify({
|
||||
// 可显示列index, 原始配置中存在 toggled: false 的列不持久化
|
||||
toggledColumnIndex: self.activeToggaleColumns
|
||||
.filter(item => !(item.pristine?.toggled === false))
|
||||
.map(item => item.index),
|
||||
// 可显示列index
|
||||
toggledColumnIndex: self.activeToggaleColumns.map(item => item.index),
|
||||
// 列排序,name,label可能不存在
|
||||
columnOrder: self.columnsData.map(
|
||||
item => item.name || item.label || item.rawIndex
|
||||
@ -1606,10 +1751,11 @@ export const TableStore = iRendererStore
|
||||
|
||||
return {
|
||||
setTable,
|
||||
getTable,
|
||||
update,
|
||||
updateColumns,
|
||||
initTableWidth,
|
||||
syncTableWidth,
|
||||
invalidTableColumnWidth,
|
||||
initRows,
|
||||
updateSelected,
|
||||
toggleAll,
|
||||
|
@ -502,3 +502,30 @@ export function warning(cat: Category, msg: string, ext?: object) {
|
||||
console.groupEnd();
|
||||
store.logs.push(log);
|
||||
}
|
||||
|
||||
// 辅助定位是因为什么属性变化导致了组件更新
|
||||
export function traceProps(props: any, prevProps: any, componentName: string) {
|
||||
console.log(
|
||||
componentName,
|
||||
Object.keys(props)
|
||||
.map(key => {
|
||||
if (props[key] !== prevProps[key]) {
|
||||
if (key === 'data') {
|
||||
return `data[${Object.keys(props[key])
|
||||
.map(item => {
|
||||
if (props[key][item] !== prevProps[key][item]) {
|
||||
return `data.${item}`;
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.filter(item => item)
|
||||
.join(', ')}]`;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.filter(item => item)
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import {isObservable, isObservableArray} from 'mobx';
|
||||
import uniq from 'lodash/uniq'
|
||||
import last from 'lodash/last'
|
||||
import merge from 'lodash/merge'
|
||||
import isPlainObject from 'lodash/isPlainObject'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import isNaN from 'lodash/isNaN'
|
||||
import isNumber from 'lodash/isNumber'
|
||||
import isString from 'lodash/isString'
|
||||
import uniq from 'lodash/uniq';
|
||||
import last from 'lodash/last';
|
||||
import merge from 'lodash/merge';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import isNaN from 'lodash/isNaN';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import isString from 'lodash/isString';
|
||||
import qs from 'qs';
|
||||
|
||||
import type {Schema, PlainObject, FunctionPropertyNames} from '../types';
|
||||
@ -20,7 +20,8 @@ import {autobindMethod} from './autobind';
|
||||
import {
|
||||
isPureVariable,
|
||||
resolveVariable,
|
||||
resolveVariableAndFilter
|
||||
resolveVariableAndFilter,
|
||||
tokenize
|
||||
} from './tpl-builtin';
|
||||
import {
|
||||
cloneObject,
|
||||
@ -196,9 +197,37 @@ export function anyChanged(
|
||||
to: {[propName: string]: any},
|
||||
strictMode: boolean = true
|
||||
): boolean {
|
||||
return (typeof attrs === 'string' ? attrs.split(/\s*,\s*/) : attrs).some(
|
||||
key => (strictMode ? from[key] !== to[key] : from[key] != to[key])
|
||||
);
|
||||
return (
|
||||
typeof attrs === 'string'
|
||||
? attrs.split(',').map(item => item.trim())
|
||||
: attrs
|
||||
).some(key => (strictMode ? from[key] !== to[key] : from[key] != to[key]));
|
||||
}
|
||||
|
||||
type Mutable<T> = {
|
||||
-readonly [k in keyof T]: T[k];
|
||||
};
|
||||
|
||||
export function changedEffect<T extends Record<string, any>>(
|
||||
attrs: string | Array<string>,
|
||||
origin: T,
|
||||
data: T,
|
||||
effect: (changes: Partial<Mutable<T>>) => void,
|
||||
strictMode: boolean = true
|
||||
) {
|
||||
const changes: Partial<T> = {};
|
||||
const keys =
|
||||
typeof attrs === 'string'
|
||||
? attrs.split(',').map(item => item.trim())
|
||||
: attrs;
|
||||
|
||||
keys.forEach(key => {
|
||||
if (strictMode ? origin[key] !== data[key] : origin[key] != data[key]) {
|
||||
(changes as any)[key] = data[key];
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(changes).length && effect(changes);
|
||||
}
|
||||
|
||||
export function rmUndefined(obj: PlainObject) {
|
||||
@ -1868,13 +1897,17 @@ export function hashCode(s: string): number {
|
||||
*/
|
||||
export function JSONTraverse(
|
||||
json: any,
|
||||
mapper: (value: any, key: string | number, host: Object) => any
|
||||
mapper: (value: any, key: string | number, host: Object) => any,
|
||||
maxDeep: number = Number.MAX_VALUE
|
||||
) {
|
||||
if (maxDeep <= 0) {
|
||||
return;
|
||||
}
|
||||
Object.keys(json).forEach(key => {
|
||||
const value: any = json[key];
|
||||
if (!isObservable(value)) {
|
||||
if (isPlainObject(value) || Array.isArray(value)) {
|
||||
JSONTraverse(value, mapper);
|
||||
JSONTraverse(value, mapper, maxDeep - 1);
|
||||
} else {
|
||||
mapper(value, key, json);
|
||||
}
|
||||
@ -2045,3 +2078,99 @@ export function differenceFromAll<T>(
|
||||
differenceFromAllCache.res = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于 schema 自动提取 trackExpression
|
||||
* 可能会不准确,建议用户自己配置
|
||||
* @param schema
|
||||
* @returns
|
||||
*/
|
||||
export function buildTrackExpression(schema: any) {
|
||||
if (!isPlainObject(schema) && !Array.isArray(schema)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const trackExpressions: Array<string> = [];
|
||||
JSONTraverse(
|
||||
schema,
|
||||
(value, key: string) => {
|
||||
if (typeof value !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 'name') {
|
||||
trackExpressions.push(isPureVariable(value) ? value : `\${${value}}`);
|
||||
} else if (key === 'source') {
|
||||
trackExpressions.push(value);
|
||||
} else if (
|
||||
key.endsWith('On') ||
|
||||
key === 'condition' ||
|
||||
key === 'trackExpression'
|
||||
) {
|
||||
trackExpressions.push(
|
||||
value.startsWith('${') ? value : `<script>${value}</script>`
|
||||
);
|
||||
} else if (value.includes('$')) {
|
||||
trackExpressions.push(value);
|
||||
}
|
||||
},
|
||||
10 // 最多遍历 10 层
|
||||
);
|
||||
|
||||
return trackExpressions.join('|');
|
||||
}
|
||||
|
||||
export function evalTrackExpression(
|
||||
expression: string,
|
||||
data: Record<string, any>
|
||||
) {
|
||||
if (typeof expression !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const parts: Array<{
|
||||
type: 'text' | 'script';
|
||||
value: string;
|
||||
}> = [];
|
||||
while (true) {
|
||||
// 这个是自动提取的时候才会用到,用户配置不要用到这个语法
|
||||
const idx = expression.indexOf('<script>');
|
||||
if (idx === -1) {
|
||||
break;
|
||||
}
|
||||
const endIdx = expression.indexOf('</script>');
|
||||
if (endIdx === -1) {
|
||||
throw new Error(
|
||||
'Invalid trackExpression miss end script token `</script>`'
|
||||
);
|
||||
}
|
||||
if (idx) {
|
||||
parts.push({
|
||||
type: 'text',
|
||||
value: expression.substring(0, idx)
|
||||
});
|
||||
}
|
||||
|
||||
parts.push({
|
||||
type: 'script',
|
||||
value: expression.substring(idx + 8, endIdx)
|
||||
});
|
||||
expression = expression.substring(endIdx + 9);
|
||||
}
|
||||
|
||||
expression &&
|
||||
parts.push({
|
||||
type: 'text',
|
||||
value: expression
|
||||
});
|
||||
|
||||
return parts
|
||||
.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return tokenize(item.value, data);
|
||||
}
|
||||
|
||||
return evalExpression(item.value, data);
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
@ -3,16 +3,25 @@
|
||||
* @author fex
|
||||
*/
|
||||
|
||||
type EventType = 'both' | 'width' | 'height';
|
||||
class EventQueue {
|
||||
q: Array<Function> = [];
|
||||
q: Array<{
|
||||
fn: Function;
|
||||
type: EventType;
|
||||
}> = [];
|
||||
|
||||
add(cb: Function) {
|
||||
this.q.push(cb);
|
||||
add(cb: Function, type: EventType = 'both') {
|
||||
this.q.push({
|
||||
fn: cb,
|
||||
type
|
||||
});
|
||||
}
|
||||
|
||||
call(...args: Array<any>) {
|
||||
this.q.forEach(fn => {
|
||||
fn(...args);
|
||||
call(type: EventType, ...args: any[]) {
|
||||
this.q.forEach(item => {
|
||||
if (item.type === type || type === 'both') {
|
||||
item.fn.apply(null, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -28,15 +37,19 @@ export function getComputedStyle(element: HTMLElement, prop: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function attachResizeEvent(element: HTMLElement, resized: () => void) {
|
||||
function attachResizeEvent(
|
||||
element: HTMLElement,
|
||||
resized: () => void,
|
||||
type: EventType = 'both'
|
||||
) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (!(element as any).resizedAttached) {
|
||||
(element as any).resizedAttached = new EventQueue();
|
||||
(element as any).resizedAttached.add(resized);
|
||||
(element as any).resizedAttached.add(resized, type);
|
||||
} else if ((element as any).resizedAttached) {
|
||||
(element as any).resizedAttached.add(resized);
|
||||
(element as any).resizedAttached.add(resized, type);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,9 +111,9 @@ function attachResizeEvent(element: HTMLElement, resized: () => void) {
|
||||
|
||||
reset();
|
||||
|
||||
let changed = function () {
|
||||
let changed = function (type: EventType = 'both') {
|
||||
if ((element as any).resizedAttached) {
|
||||
(element as any).resizedAttached.call();
|
||||
(element as any).resizedAttached.call(type);
|
||||
}
|
||||
};
|
||||
|
||||
@ -121,11 +134,16 @@ function attachResizeEvent(element: HTMLElement, resized: () => void) {
|
||||
};
|
||||
|
||||
let onScroll = function (e: Event) {
|
||||
if (
|
||||
element.offsetWidth != lastWidth ||
|
||||
element.offsetHeight != lastHeight
|
||||
) {
|
||||
changed();
|
||||
const widthChanged = element.offsetWidth != lastWidth;
|
||||
const heightChanged = element.offsetHeight != lastHeight;
|
||||
if (widthChanged || heightChanged) {
|
||||
changed(
|
||||
widthChanged && heightChanged
|
||||
? 'both'
|
||||
: widthChanged
|
||||
? 'width'
|
||||
: 'height'
|
||||
);
|
||||
}
|
||||
reset();
|
||||
};
|
||||
@ -159,15 +177,12 @@ function detach(element: HTMLElement) {
|
||||
}
|
||||
}
|
||||
|
||||
export function resizeSensor(
|
||||
element: HTMLElement,
|
||||
callback: () => void
|
||||
): () => void;
|
||||
export function resizeSensor(
|
||||
element: HTMLElement,
|
||||
callback: () => void,
|
||||
once: boolean = false
|
||||
) {
|
||||
once: boolean = false,
|
||||
type: EventType = 'both'
|
||||
): () => void {
|
||||
if (!element) {
|
||||
return () => {};
|
||||
}
|
||||
@ -175,15 +190,19 @@ export function resizeSensor(
|
||||
let disposeEvent: (() => void) | undefined = undefined;
|
||||
|
||||
if (once) {
|
||||
disposeEvent = attachResizeEvent(element, function (this: any) {
|
||||
callback.apply(this, arguments);
|
||||
disposeEvent?.();
|
||||
detach(element);
|
||||
});
|
||||
return;
|
||||
disposeEvent = attachResizeEvent(
|
||||
element,
|
||||
function (this: any) {
|
||||
callback.apply(this, arguments);
|
||||
disposeEvent?.();
|
||||
detach(element);
|
||||
},
|
||||
type
|
||||
);
|
||||
return () => {};
|
||||
}
|
||||
|
||||
disposeEvent = attachResizeEvent(element, callback);
|
||||
disposeEvent = attachResizeEvent(element, callback, type);
|
||||
let detached = false;
|
||||
|
||||
return function () {
|
||||
|
@ -67,7 +67,7 @@
|
||||
"@svgr/rollup": "^6.2.1",
|
||||
"@types/async": "^2.0.45",
|
||||
"@types/classnames": "^2.2.3",
|
||||
"@types/codemirror": "^5.60.5",
|
||||
"@types/codemirror": "5.60.10",
|
||||
"@types/deep-diff": "^1.0.0",
|
||||
"@types/history": "^4.6.0",
|
||||
"@types/hoist-non-react-statics": "^3.0.1",
|
||||
|
@ -57,7 +57,7 @@
|
||||
"@svgr/rollup": "^6.2.1",
|
||||
"@types/async": "^2.0.45",
|
||||
"@types/classnames": "^2.2.3",
|
||||
"@types/codemirror": "^5.60.5",
|
||||
"@types/codemirror": "5.60.10",
|
||||
"@types/deep-diff": "^1.0.0",
|
||||
"@types/history": "^4.6.0",
|
||||
"@types/hoist-non-react-statics": "^3.0.1",
|
||||
|
@ -4008,6 +4008,7 @@
|
||||
--Table-toolbar-marginX: #{px2rem(4px)};
|
||||
--Table-toolbar-marginY: var(--gap-base);
|
||||
--Table-tree-borderColor: var(--colors-neutral-line-8);
|
||||
--Table-tree-indent: var(--gap-lg);
|
||||
--Table-searchableForm-backgroundColor: #f6f7f8;
|
||||
--Table-searchableForm-borderRadius: #{px2rem(4px)};
|
||||
--Table-empty-icon-size: #{px2rem(74px)};
|
||||
|
@ -15,4 +15,8 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .visibility-sensor {
|
||||
height: 100%; // 修复图表高度为 0 时,visibility-sensor 无法触发的问题
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,10 @@
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
&-heading {
|
||||
@ -360,6 +364,7 @@
|
||||
|
||||
.#{$ns}TableCell--title {
|
||||
min-width: fit-content;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -547,42 +552,6 @@
|
||||
background: var(--Table-tree-borderColor);
|
||||
}
|
||||
|
||||
@for $i from 2 through 10 {
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-expanded {
|
||||
.#{$ns}Table-expandCell:before {
|
||||
left: px2rem(23px) + px2rem(18px) * ($i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th {
|
||||
.#{$ns}Table-expandBtn {
|
||||
position: relative;
|
||||
left: px2rem(1px) + (px2rem(18px)) * ($i - 1);
|
||||
}
|
||||
|
||||
.#{$ns}Table-divider2 {
|
||||
left: px2rem(5px) + (px2rem(18px)) * ($i - 1);
|
||||
}
|
||||
|
||||
.#{$ns}Table-divider3 {
|
||||
left: px2rem(5px) + (px2rem(18px)) * ($i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-last .#{$ns}Table-divider3 {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-last:not(.is-expanded) {
|
||||
.#{$ns}Table-expandCell + td {
|
||||
&::before {
|
||||
height: 50%;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-checkCell,
|
||||
> tbody > tr > td.#{$ns}Table-checkCell {
|
||||
width: px2rem(1px);
|
||||
@ -620,18 +589,6 @@
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-expandCell {
|
||||
position: relative;
|
||||
|
||||
@for $i from 1 through 7 {
|
||||
.#{$ns}Table-divider-#{$i} {
|
||||
position: absolute;
|
||||
width: px2rem(1px);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: var(--Table-tree-borderColor);
|
||||
left: px2rem(23px) + px2rem(18px) * ($i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr.is-expanded > td.#{$ns}Table-expandCell {
|
||||
@ -656,6 +613,11 @@
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-primayCell,
|
||||
> tbody > tr > td.#{$ns}Table-primayCell {
|
||||
white-space: nowrap; // 树形表格展示标题栏,不要换行
|
||||
}
|
||||
}
|
||||
|
||||
&.is-mobile {
|
||||
@ -899,7 +861,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-expandBtn {
|
||||
&-expandBtn,
|
||||
&-expandBtn2 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--Table-expandBtn-color);
|
||||
@ -933,6 +896,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-expandBtn2 {
|
||||
margin-right: var(--gap-sm);
|
||||
}
|
||||
|
||||
&-expandBtn2 + * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-indent {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-expandSpace {
|
||||
display: inline-block;
|
||||
width: px2rem(22px);
|
||||
}
|
||||
|
||||
&-dragBtn {
|
||||
margin-right: var(--gap-xs);
|
||||
display: inline-block;
|
||||
@ -1027,6 +1007,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// table 骨架样式
|
||||
&-emptyBlock {
|
||||
background-color: #eaebed;
|
||||
border-radius: 5px;
|
||||
line-height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}InputTable {
|
||||
@ -1074,6 +1061,7 @@
|
||||
> .#{$ns}Button,
|
||||
> .#{$ns}Button--disabled-wrap > .#{$ns}Button {
|
||||
margin: px2rem(3px);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
> .#{$ns}Button--disabled-wrap > .#{$ns}Button--link {
|
||||
|
@ -1374,9 +1374,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
<span
|
||||
className={cx('Table-row-indent', `indent-level-${levels.length}`)}
|
||||
style={
|
||||
levels.length > 0
|
||||
? {paddingLeft: indentSize * levels.length + 'px'}
|
||||
: {}
|
||||
levels.length > 0 ? {width: indentSize * levels.length + 'px'} : {}
|
||||
}
|
||||
></span>
|
||||
) : null;
|
||||
|
@ -25,10 +25,10 @@ exports[`doAction:crud reload 1`] = `
|
||||
</button>
|
||||
<div
|
||||
class="cxd-Crud is-loading"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -259,47 +259,46 @@ exports[`doAction:crud reload 1`] = `
|
||||
class="cxd-Spinner-icon cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -334,10 +333,10 @@ exports[`doAction:crud reload with data1 1`] = `
|
||||
</button>
|
||||
<div
|
||||
class="cxd-Crud is-loading"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -568,47 +567,46 @@ exports[`doAction:crud reload with data1 1`] = `
|
||||
class="cxd-Spinner-icon cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -643,10 +641,10 @@ exports[`doAction:crud reload with data2 1`] = `
|
||||
</button>
|
||||
<div
|
||||
class="cxd-Crud is-loading"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; --Table-column-6-width: 0px; --Table-column-7-width: 0px; --Table-column-8-width: 0px; --Table-column-9-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -877,47 +875,46 @@ exports[`doAction:crud reload with data2 1`] = `
|
||||
class="cxd-Spinner-icon cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -112,3 +112,22 @@ export function replaceReactAriaIds(container: HTMLElement) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mock IntersectionObserver
|
||||
class IntersectionObserver {
|
||||
observe = jest.fn();
|
||||
disconnect = jest.fn();
|
||||
unobserve = jest.fn();
|
||||
}
|
||||
|
||||
Object.defineProperty(window, 'IntersectionObserver', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: IntersectionObserver
|
||||
});
|
||||
|
||||
Object.defineProperty(global, 'IntersectionObserver', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: IntersectionObserver
|
||||
});
|
||||
|
@ -51,10 +51,10 @@ exports[`Renderer:input table 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-InputTable cxd-Form-control"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-contentWrap"
|
||||
@ -195,47 +195,46 @@ exports[`Renderer:input table 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -417,10 +416,10 @@ exports[`Renderer:input table add 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-InputTable cxd-Form-control"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-contentWrap"
|
||||
@ -440,7 +439,7 @@ exports[`Renderer:input table add 1`] = `
|
||||
/>
|
||||
<col
|
||||
data-index="5"
|
||||
style="width: 100px;"
|
||||
style="width: 150px; min-width: 150px;"
|
||||
/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
@ -520,34 +519,24 @@ exports[`Renderer:input table add 1`] = `
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="cxd-Form cxd-Form--normal cxd-Form--quickEdit"
|
||||
novalidate=""
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
value="aa"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<div
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="a"
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value="bb"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="a"
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value="aa"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -556,33 +545,24 @@ exports[`Renderer:input table add 1`] = `
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="cxd-Form cxd-Form--normal cxd-Form--quickEdit"
|
||||
novalidate=""
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<div
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="b"
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="b"
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value="bb"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -618,7 +598,45 @@ exports[`Renderer:input table add 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -636,45 +654,6 @@ exports[`Renderer:input table add 1`] = `
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -789,10 +768,10 @@ exports[`Renderer:input-table cell selects delete 1`] = `
|
||||
</label>
|
||||
<div
|
||||
class="cxd-InputTable cxd-Form-control"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-contentWrap"
|
||||
@ -867,74 +846,65 @@ exports[`Renderer:input-table cell selects delete 1`] = `
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="cxd-Form cxd-Form--normal cxd-Form--quickEdit"
|
||||
novalidate=""
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
class="cxd-SelectControl cxd-Form-control"
|
||||
>
|
||||
<div
|
||||
class="cxd-SelectControl cxd-Form-control"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
class="cxd-Select cxd-Select--multi"
|
||||
role="combobox"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
class="cxd-Select cxd-Select--multi"
|
||||
role="combobox"
|
||||
tabindex="0"
|
||||
class="cxd-Select-valueWrap"
|
||||
>
|
||||
<div
|
||||
class="cxd-Select-valueWrap"
|
||||
class="cxd-Select-value"
|
||||
>
|
||||
<div
|
||||
class="cxd-Select-value"
|
||||
<span
|
||||
class="cxd-Select-valueLabel"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-valueLabel"
|
||||
>
|
||||
s2
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Select-valueIcon"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Select-value"
|
||||
s2
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Select-valueIcon"
|
||||
>
|
||||
<span
|
||||
class="cxd-Select-valueLabel"
|
||||
>
|
||||
s3
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Select-valueIcon"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="cxd-Select-arrow"
|
||||
<div
|
||||
class="cxd-Select-value"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-right-arrow-bold"
|
||||
icon="right-arrow-bold"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Select-valueLabel"
|
||||
>
|
||||
s3
|
||||
</span>
|
||||
<span
|
||||
class="cxd-Select-valueIcon"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="cxd-Select-arrow"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-right-arrow-bold"
|
||||
icon="right-arrow-bold"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -956,47 +926,46 @@ exports[`Renderer:input-table cell selects delete 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1210,10 +1179,10 @@ exports[`Renderer:input-table with combo column 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-InputTable cxd-Form-control"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table"
|
||||
style="--Table-column-3-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-contentWrap"
|
||||
@ -1265,128 +1234,119 @@ exports[`Renderer:input-table with combo column 1`] = `
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="cxd-Form cxd-Form--normal cxd-Form--quickEdit"
|
||||
novalidate=""
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--normal"
|
||||
data-role="form-item"
|
||||
class="cxd-ComboControl cxd-Form-control"
|
||||
>
|
||||
<div
|
||||
class="cxd-ComboControl cxd-Form-control"
|
||||
class="cxd-Combo cxd-Combo--single cxd-Combo--hor"
|
||||
>
|
||||
<div
|
||||
class="cxd-Combo cxd-Combo--single cxd-Combo--hor"
|
||||
class="cxd-Combo-item"
|
||||
>
|
||||
<div
|
||||
class="cxd-Combo-item"
|
||||
class="cxd-Combo-itemInner"
|
||||
>
|
||||
<div
|
||||
class="cxd-Combo-itemInner"
|
||||
class="cxd-Form cxd-Form--row cxd-Combo-form"
|
||||
novalidate=""
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form cxd-Form--row cxd-Combo-form"
|
||||
novalidate=""
|
||||
class="cxd-Form-row"
|
||||
>
|
||||
<input
|
||||
style="display: none;"
|
||||
type="submit"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-row"
|
||||
class="cxd-Form-col"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-col"
|
||||
class="cxd-Form-item cxd-Form-item--row"
|
||||
data-role="form-item"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--row"
|
||||
data-role="form-item"
|
||||
class="cxd-Form-rowInner"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-rowInner"
|
||||
class="cxd-NumberControl cxd-Form-control"
|
||||
>
|
||||
<div
|
||||
class="cxd-NumberControl cxd-Form-control"
|
||||
class="cxd-Number cxd-Number--borderFull"
|
||||
>
|
||||
<div
|
||||
class="cxd-Number cxd-Number--borderFull"
|
||||
class="cxd-Number-handler-wrap"
|
||||
>
|
||||
<div
|
||||
class="cxd-Number-handler-wrap"
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="cxd-Number-handler cxd-Number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="cxd-Number-handler cxd-Number-handler-up"
|
||||
role="button"
|
||||
class="cxd-Number-handler-up-inner"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="cxd-Number-handler-up-inner"
|
||||
unselectable="on"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="cxd-Number-handler cxd-Number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="cxd-Number-handler-down-inner"
|
||||
unselectable="on"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-valuenow="88"
|
||||
autocomplete="off"
|
||||
class="cxd-Number-input"
|
||||
placeholder="请手动输入分数"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value="88"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="cxd-Number-handler cxd-Number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="cxd-Number-handler-down-inner"
|
||||
unselectable="on"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Number-input-wrap"
|
||||
>
|
||||
<input
|
||||
aria-valuenow="88"
|
||||
autocomplete="off"
|
||||
class="cxd-Number-input"
|
||||
placeholder="请手动输入分数"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value="88"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Form-col"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-col"
|
||||
class="cxd-Form-item cxd-Form-item--row"
|
||||
data-role="form-item"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--row"
|
||||
data-role="form-item"
|
||||
class="cxd-Form-rowInner"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-rowInner"
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
>
|
||||
<div
|
||||
class="cxd-Form-control cxd-TextControl"
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<div
|
||||
class="cxd-TextControl-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="comment"
|
||||
placeholder="请手动输入意见"
|
||||
size="10"
|
||||
type="text"
|
||||
value="this is comment msg!!"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class=""
|
||||
name="comment"
|
||||
placeholder="请手动输入意见"
|
||||
size="10"
|
||||
type="text"
|
||||
value="this is comment msg!!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1404,47 +1364,46 @@ exports[`Renderer:input-table with combo column 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,10 +17,10 @@ exports[`1. Renderer:crud basic interval headerToolbar footerToolbar 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-Crud"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -487,7 +487,6 @@ exports[`1. Renderer:crud basic interval headerToolbar footerToolbar 1`] = `
|
||||
class="cxd-Spinner-icon cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Table-toolbar cxd-Table-footToolbar"
|
||||
@ -793,45 +792,45 @@ exports[`1. Renderer:crud basic interval headerToolbar footerToolbar 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -2148,10 +2147,10 @@ exports[`6. Renderer:crud source & alwaysShowPagination 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-Crud"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; --Table-column-5-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -2577,7 +2576,6 @@ exports[`6. Renderer:crud source & alwaysShowPagination 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Table-toolbar cxd-Table-footToolbar"
|
||||
@ -2641,45 +2639,45 @@ exports[`6. Renderer:crud source & alwaysShowPagination 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -2706,7 +2704,6 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
|
||||
>
|
||||
<div
|
||||
class="cxd-Crud"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Crud-selection"
|
||||
@ -2796,6 +2793,7 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Table cxd-Crud-body"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative; --Table-column-1-width: 0px;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -3454,7 +3452,45 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Table-toolbar cxd-Table-footToolbar"
|
||||
@ -3610,45 +3646,6 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,6 @@ exports[`Renderer:Pagination 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-PaginationWrapper"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Pagination-wrap cxd-PaginationWrapper-pager"
|
||||
@ -84,6 +83,7 @@ exports[`Renderer:Pagination 1`] = `
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Table"
|
||||
style="--Table-column-3-width: 0px; --Table-column-4-width: 0px; position: relative;"
|
||||
>
|
||||
<div
|
||||
class="cxd-Table-fixedTop"
|
||||
@ -210,47 +210,46 @@ exports[`Renderer:Pagination 1`] = `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<span />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
class="resize-sensor"
|
||||
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
class="resize-sensor-expand"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="resize-sensor-shrink"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
|
||||
/>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="resize-sensor-appear"
|
||||
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,8 @@
|
||||
"tslib": "^2.3.1",
|
||||
"video-react": "0.15.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"react-error-boundary": "^4.0.11"
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-intersection-observer": "9.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
@ -85,7 +86,7 @@
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@types/async": "^2.0.45",
|
||||
"@types/codemirror": "^5.60.3",
|
||||
"@types/codemirror": "5.60.10",
|
||||
"@types/echarts": "^4.9.2",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/history": "^4.6.0",
|
||||
|
@ -58,6 +58,7 @@ import {
|
||||
import type {PaginationProps} from './Pagination';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import memoize from 'lodash/memoize';
|
||||
|
||||
export type CRUDBultinToolbarType =
|
||||
| 'columns-toggler'
|
||||
@ -473,6 +474,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
/** 父容器, 主要用于定位CRUD内部popover的挂载点 */
|
||||
parentContainer: Element | null;
|
||||
|
||||
filterOnEvent = memoize(onEvent =>
|
||||
omitBy(onEvent, (event, key: any) => !INNER_EVENTS.includes(key))
|
||||
);
|
||||
|
||||
constructor(props: CRUDProps) {
|
||||
super(props);
|
||||
|
||||
@ -548,8 +553,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {store, autoGenerateFilter, columns} = this.props;
|
||||
if (this.props.perPage) {
|
||||
const {store, autoGenerateFilter, perPageField, columns} = this.props;
|
||||
if (this.props.perPage && !store.query[perPageField || 'perPage']) {
|
||||
store.changePage(store.page, this.props.perPage);
|
||||
}
|
||||
|
||||
@ -662,6 +667,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
clearTimeout(this.timer);
|
||||
this.filterOnEvent.cache.clear?.();
|
||||
}
|
||||
|
||||
/** 查找CRUD最近层级的父窗口 */
|
||||
@ -2451,10 +2457,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
...rest,
|
||||
// 通用事件 例如cus-event 如果直接透传给table 则会被触发2次
|
||||
// 因此只将下层组件table、cards中自定义事件透传下去 否则通过crud配置了也不会执行
|
||||
onEvent: omitBy(
|
||||
onEvent,
|
||||
(event, key: any) => !INNER_EVENTS.includes(key)
|
||||
),
|
||||
onEvent: this.filterOnEvent(onEvent),
|
||||
columns: store.columns ?? rest.columns,
|
||||
type: mode || 'table'
|
||||
},
|
||||
|
@ -19,7 +19,6 @@ import {
|
||||
ApiObject,
|
||||
autobind,
|
||||
isExpression,
|
||||
ITableStore,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
getRendererByName,
|
||||
@ -283,9 +282,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
entries: SimpleMap<any, number>;
|
||||
entityId: number = 1;
|
||||
subForms: any = {};
|
||||
subFormItems: any = {};
|
||||
rowPrinstine: Array<any> = [];
|
||||
editting: any = {};
|
||||
tableStore?: ITableStore;
|
||||
|
||||
constructor(props: TableProps) {
|
||||
super(props);
|
||||
@ -305,6 +304,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
this.handleRadioChange = this.handleRadioChange.bind(this);
|
||||
this.getEntryId = this.getEntryId.bind(this);
|
||||
this.subFormRef = this.subFormRef.bind(this);
|
||||
this.subFormItemRef = this.subFormItemRef.bind(this);
|
||||
this.handlePageChange = this.handlePageChange.bind(this);
|
||||
this.emitValue = this.emitValue.bind(this);
|
||||
}
|
||||
@ -384,6 +384,10 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
this.subForms[`${x}-${y}`] = form;
|
||||
}
|
||||
|
||||
subFormItemRef(form: any, x: number, y: number) {
|
||||
this.subFormItems[`${x}-${y}`] = form;
|
||||
}
|
||||
|
||||
async validate(): Promise<string | void> {
|
||||
const {value, translate: __, columns} = this.props;
|
||||
const minLength = this.resolveVariableProps(this.props, 'minLength');
|
||||
@ -444,16 +448,18 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.tableStore) return;
|
||||
|
||||
// 校验子项
|
||||
const children = this.tableStore.children.filter(
|
||||
item => item?.storeType === 'FormItemStore'
|
||||
const subFormItemss: Array<any> = [];
|
||||
Object.keys(this.subFormItems).forEach(
|
||||
key =>
|
||||
this.subFormItems[key] && subFormItemss.push(this.subFormItems[key])
|
||||
);
|
||||
|
||||
const results = await Promise.all(
|
||||
children.map(item => item.validate(this.props.value))
|
||||
subFormItemss.map(item => item.props.onValidate())
|
||||
);
|
||||
let msg = ~results.indexOf(false) ? __('Form.validateFailed') : '';
|
||||
return msg;
|
||||
}
|
||||
|
||||
async emitValue() {
|
||||
@ -731,6 +737,12 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
);
|
||||
subForms.forEach(form => form.flush());
|
||||
|
||||
const subFormItems: Array<any> = [];
|
||||
Object.keys(this.subFormItems).forEach(
|
||||
key => this.subFormItems[key] && subFormItems.push(this.subFormItems[key])
|
||||
);
|
||||
subFormItems.forEach(item => item.props.onFlushChange?.());
|
||||
|
||||
const validateForms: Array<any> = [];
|
||||
Object.keys(this.subForms).forEach(key => {
|
||||
const arr = key.split('-');
|
||||
@ -1234,7 +1246,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
label: __('Table.operation'),
|
||||
className: 'v-middle nowrap',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
width: 150,
|
||||
innerClassName: 'm-n'
|
||||
};
|
||||
columns.push(operation);
|
||||
@ -1473,7 +1485,6 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
while (ref && ref.getWrappedInstance) {
|
||||
ref = ref.getWrappedInstance();
|
||||
}
|
||||
this.tableStore = ref?.props?.store;
|
||||
}
|
||||
|
||||
computedAddBtnDisabled() {
|
||||
@ -1558,6 +1569,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
onSaveOrder: this.handleSaveTableOrder,
|
||||
buildItemProps: this.buildItemProps,
|
||||
quickEditFormRef: this.subFormRef,
|
||||
quickEditFormItemRef: this.subFormItemRef,
|
||||
columnsTogglable: columnsTogglable,
|
||||
combineNum: combineNum,
|
||||
combineFromIndex: combineFromIndex,
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {RendererProps, noop} from 'amis-core';
|
||||
import {RendererProps, getRendererByName, noop} from 'amis-core';
|
||||
import hoistNonReactStatic from 'hoist-non-react-statics';
|
||||
import {ActionObject} from 'amis-core';
|
||||
import keycode from 'keycode';
|
||||
@ -121,8 +121,10 @@ export const HocQuickEdit =
|
||||
this.handleWindowKeyPress = this.handleWindowKeyPress.bind(this);
|
||||
this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this);
|
||||
this.formRef = this.formRef.bind(this);
|
||||
this.formItemRef = this.formItemRef.bind(this);
|
||||
this.handleInit = this.handleInit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleFormItemChange = this.handleFormItemChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
isOpened: false
|
||||
@ -152,6 +154,17 @@ export const HocQuickEdit =
|
||||
quickEditFormRef(ref, colIndex, rowIndex);
|
||||
}
|
||||
}
|
||||
formItemRef(ref: any) {
|
||||
const {quickEditFormItemRef, rowIndex, colIndex} = this.props;
|
||||
|
||||
if (quickEditFormItemRef) {
|
||||
while (ref && ref.getWrappedInstance) {
|
||||
ref = ref.getWrappedInstance();
|
||||
}
|
||||
|
||||
quickEditFormItemRef(ref, colIndex, rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
handleWindowKeyPress(e: Event) {
|
||||
const ns = this.props.classPrefix;
|
||||
@ -337,6 +350,17 @@ export const HocQuickEdit =
|
||||
);
|
||||
}
|
||||
|
||||
handleFormItemChange(value: any) {
|
||||
const {onQuickChange, quickEdit, name} = this.props;
|
||||
|
||||
onQuickChange(
|
||||
{[name!]: value},
|
||||
(quickEdit as QuickEditConfig).saveImmediately,
|
||||
false,
|
||||
quickEdit as QuickEditConfig
|
||||
);
|
||||
}
|
||||
|
||||
openQuickEdit() {
|
||||
currentOpened = this;
|
||||
this.setState({
|
||||
@ -526,6 +550,51 @@ export const HocQuickEdit =
|
||||
);
|
||||
}
|
||||
|
||||
renderInlineForm() {
|
||||
const {
|
||||
render,
|
||||
classnames: cx,
|
||||
canAccessSuperData,
|
||||
disabled,
|
||||
value,
|
||||
name
|
||||
} = this.props;
|
||||
|
||||
const schema: any = this.buildSchema();
|
||||
|
||||
// 有且只有一个表单项时,直接渲染表单项
|
||||
if (
|
||||
Array.isArray(schema.body) &&
|
||||
schema.body.length === 1 &&
|
||||
!schema.body[0].unique && // 唯一模式还不支持
|
||||
!schema.body[0].value && // 不能有默认值表达式什么的情况
|
||||
schema.body[0].name &&
|
||||
schema.body[0].name === name &&
|
||||
schema.body[0].type &&
|
||||
getRendererByName(schema.body[0].type)?.isFormItem
|
||||
) {
|
||||
return render('inline-form-item', schema.body[0], {
|
||||
mode: 'normal',
|
||||
value: value || '',
|
||||
onChange: this.handleFormItemChange,
|
||||
ref: this.formItemRef
|
||||
});
|
||||
}
|
||||
|
||||
return render('inline-form', schema, {
|
||||
value: undefined,
|
||||
wrapperComponent: 'div',
|
||||
className: cx('Form--quickEdit'),
|
||||
ref: this.formRef,
|
||||
simpleMode: true,
|
||||
onInit: this.handleInit,
|
||||
onChange: this.handleChange,
|
||||
formLazyChange: false,
|
||||
canAccessSuperData,
|
||||
disabled
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
onQuickChange,
|
||||
@ -556,20 +625,7 @@ export const HocQuickEdit =
|
||||
(quickEdit as QuickEditConfig).isFormMode
|
||||
) {
|
||||
return (
|
||||
<Component {...this.props}>
|
||||
{render('inline-form', this.buildSchema(), {
|
||||
value: undefined,
|
||||
wrapperComponent: 'div',
|
||||
className: cx('Form--quickEdit'),
|
||||
ref: this.formRef,
|
||||
simpleMode: true,
|
||||
onInit: this.handleInit,
|
||||
onChange: this.handleChange,
|
||||
formLazyChange: false,
|
||||
canAccessSuperData,
|
||||
disabled
|
||||
})}
|
||||
</Component>
|
||||
<Component {...this.props}>{this.renderInlineForm()}</Component>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
|
244
packages/amis/src/renderers/Table/AutoFilterForm.tsx
Normal file
244
packages/amis/src/renderers/Table/AutoFilterForm.tsx
Normal file
@ -0,0 +1,244 @@
|
||||
import {
|
||||
IColumn,
|
||||
ITableStore,
|
||||
RendererProps,
|
||||
createObject,
|
||||
padArr
|
||||
} from 'amis-core';
|
||||
import {Icon} from 'amis-ui';
|
||||
import {observer} from 'mobx-react';
|
||||
import React from 'react';
|
||||
|
||||
export interface AutoFilterFormProps extends RendererProps {
|
||||
searchFormExpanded: boolean;
|
||||
autoGenerateFilter: any;
|
||||
activedSearchableColumns: Array<IColumn>;
|
||||
searchableColumns: Array<IColumn>;
|
||||
columnsNum: number;
|
||||
onItemToggleExpanded?: (column: IColumn, value: any) => void;
|
||||
onToggleExpanded?: () => void;
|
||||
query?: any;
|
||||
|
||||
onSearchableFromReset?: any;
|
||||
onSearchableFromSubmit?: any;
|
||||
onSearchableFromInit?: any;
|
||||
}
|
||||
|
||||
export function AutoFilterForm({
|
||||
autoGenerateFilter,
|
||||
searchFormExpanded,
|
||||
activedSearchableColumns,
|
||||
searchableColumns,
|
||||
onItemToggleExpanded,
|
||||
onToggleExpanded,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
render,
|
||||
data,
|
||||
onSearchableFromReset,
|
||||
onSearchableFromSubmit,
|
||||
onSearchableFromInit
|
||||
}: AutoFilterFormProps) {
|
||||
const schema = React.useMemo(() => {
|
||||
const {columnsNum, showBtnToolbar} =
|
||||
typeof autoGenerateFilter === 'boolean'
|
||||
? {
|
||||
columnsNum: 3,
|
||||
showBtnToolbar: true
|
||||
}
|
||||
: autoGenerateFilter;
|
||||
|
||||
const body: Array<any> = padArr(activedSearchableColumns, columnsNum).map(
|
||||
group => ({
|
||||
type: 'group',
|
||||
body: group.map((column: any) => ({
|
||||
...(column.searchable === true
|
||||
? {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
label: column.label
|
||||
}
|
||||
: {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
...column.searchable
|
||||
}),
|
||||
name: column.searchable?.name ?? column.name,
|
||||
label: column.searchable?.label ?? column.label
|
||||
}))
|
||||
})
|
||||
);
|
||||
|
||||
let showExpander = searchableColumns.length >= columnsNum;
|
||||
|
||||
// todo 以后做动画
|
||||
if (!searchFormExpanded && body.length) {
|
||||
body.splice(1, body.length - 1);
|
||||
body[0].body.splice(columnsNum - 1, body[0].body.length - columnsNum + 1);
|
||||
}
|
||||
|
||||
let lastGroup = body[body.length - 1];
|
||||
if (
|
||||
!Array.isArray(lastGroup?.body) ||
|
||||
lastGroup.body.length >= columnsNum
|
||||
) {
|
||||
lastGroup = {
|
||||
type: 'group',
|
||||
body: []
|
||||
};
|
||||
body.push(lastGroup);
|
||||
}
|
||||
|
||||
let count = Math.max(columnsNum - lastGroup.body.length - 1);
|
||||
while (count-- > 0) {
|
||||
lastGroup.body.push({
|
||||
type: 'tpl',
|
||||
tpl: ''
|
||||
});
|
||||
}
|
||||
lastGroup.body.push({
|
||||
type: 'container',
|
||||
className: 'ButtonToolbar text-right block',
|
||||
wrapperBody: false,
|
||||
body: [
|
||||
{
|
||||
type: 'dropdown-button',
|
||||
label: __('Table.searchFields'),
|
||||
className: cx('Table-searchableForm-dropdown', 'mr-2'),
|
||||
level: 'link',
|
||||
trigger: 'click',
|
||||
size: 'sm',
|
||||
align: 'right',
|
||||
visible: showBtnToolbar,
|
||||
buttons: searchableColumns.map(column => {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
label: false,
|
||||
className: cx('Table-searchableForm-checkbox'),
|
||||
inputClassName: cx('Table-searchableForm-checkbox-inner'),
|
||||
name: `${
|
||||
column.searchable.strategy === 'jsonql' ? '' : '__search_'
|
||||
}${column.searchable?.name ?? column.name}`,
|
||||
option: column.searchable?.label ?? column.label,
|
||||
value: column.enableSearch,
|
||||
badge: {
|
||||
offset: [-10, 5],
|
||||
visibleOn: `${
|
||||
column.toggable && !column.toggled && column.enableSearch
|
||||
}`
|
||||
},
|
||||
onChange: (value: boolean) =>
|
||||
onItemToggleExpanded?.(column, value)
|
||||
};
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
type: 'submit',
|
||||
label: __('search'),
|
||||
level: 'primary',
|
||||
className: 'w-18'
|
||||
},
|
||||
{
|
||||
type: 'reset',
|
||||
label: __('reset'),
|
||||
className: 'w-18'
|
||||
},
|
||||
|
||||
showExpander
|
||||
? {
|
||||
children: () => (
|
||||
<a
|
||||
className={cx(
|
||||
'Table-SFToggler',
|
||||
searchFormExpanded ? 'is-expanded' : ''
|
||||
)}
|
||||
onClick={onToggleExpanded}
|
||||
>
|
||||
{__(searchFormExpanded ? 'collapse' : 'expand')}
|
||||
<span className={cx('Table-SFToggler-arrow')}>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
: null
|
||||
].filter(item => item)
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'form',
|
||||
api: null,
|
||||
title: '',
|
||||
mode: 'horizontal',
|
||||
submitText: __('search'),
|
||||
body: body,
|
||||
actions: [],
|
||||
canAccessSuperData: false
|
||||
};
|
||||
}, [
|
||||
autoGenerateFilter,
|
||||
activedSearchableColumns,
|
||||
searchableColumns,
|
||||
searchFormExpanded
|
||||
]);
|
||||
|
||||
return render('searchable-form', schema, {
|
||||
key: 'searchable-form',
|
||||
panelClassName: cx('Table-searchableForm'),
|
||||
actionsClassName: cx('Table-searchableForm-footer'),
|
||||
onReset: onSearchableFromReset,
|
||||
onSubmit: onSearchableFromSubmit,
|
||||
onInit: onSearchableFromInit,
|
||||
formStore: undefined,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export default observer(
|
||||
({
|
||||
store,
|
||||
query,
|
||||
data,
|
||||
...rest
|
||||
}: Omit<
|
||||
AutoFilterFormProps,
|
||||
| 'activedSearchableColumns'
|
||||
| 'searchableColumns'
|
||||
| 'searchFormExpanded'
|
||||
| 'onItemToggleExpanded'
|
||||
| 'onToggleExpanded'
|
||||
> & {
|
||||
store: ITableStore;
|
||||
query: any;
|
||||
}) => {
|
||||
const onItemToggleExpanded = React.useCallback(
|
||||
(column: IColumn, value: any) => {
|
||||
column.setEnableSearch(value);
|
||||
store.setSearchFormExpanded(true);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const onToggleExpanded = React.useCallback(() => {
|
||||
store.toggleSearchFormExpanded();
|
||||
}, []);
|
||||
|
||||
const ctx = React.useMemo(
|
||||
() => (query ? createObject(data, query) : data),
|
||||
[query, data]
|
||||
);
|
||||
|
||||
return (
|
||||
<AutoFilterForm
|
||||
{...(rest as any)}
|
||||
activedSearchableColumns={store.activedSearchableColumns}
|
||||
searchableColumns={store.searchableColumns}
|
||||
searchFormExpanded={store.searchFormExpanded}
|
||||
onItemToggleExpanded={onItemToggleExpanded}
|
||||
onToggleExpanded={onToggleExpanded}
|
||||
data={ctx}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
236
packages/amis/src/renderers/Table/Cell.tsx
Normal file
236
packages/amis/src/renderers/Table/Cell.tsx
Normal file
@ -0,0 +1,236 @@
|
||||
import {
|
||||
IColumn,
|
||||
IRow,
|
||||
ITableStore,
|
||||
PlainObject,
|
||||
SchemaNode,
|
||||
ThemeProps,
|
||||
resolveVariable,
|
||||
buildTrackExpression,
|
||||
evalTrackExpression
|
||||
} from 'amis-core';
|
||||
import {BadgeObject, Checkbox, Icon} from 'amis-ui';
|
||||
import React from 'react';
|
||||
|
||||
export interface CellProps extends ThemeProps {
|
||||
region: string;
|
||||
column: IColumn;
|
||||
item: IRow;
|
||||
props: PlainObject;
|
||||
ignoreDrag?: boolean;
|
||||
render: (
|
||||
region: string,
|
||||
node: SchemaNode,
|
||||
props?: PlainObject
|
||||
) => JSX.Element;
|
||||
store: ITableStore;
|
||||
multiple: boolean;
|
||||
canAccessSuperData?: boolean;
|
||||
itemBadge?: BadgeObject;
|
||||
onCheck?: (item: IRow) => void;
|
||||
onDragStart?: (e: React.DragEvent) => void;
|
||||
popOverContainer?: any;
|
||||
quickEditFormRef: any;
|
||||
onImageEnlarge?: any;
|
||||
}
|
||||
|
||||
export default function Cell({
|
||||
region,
|
||||
column,
|
||||
item,
|
||||
props,
|
||||
ignoreDrag,
|
||||
render,
|
||||
store,
|
||||
multiple,
|
||||
itemBadge,
|
||||
classnames: cx,
|
||||
classPrefix: ns,
|
||||
canAccessSuperData,
|
||||
onCheck,
|
||||
onDragStart,
|
||||
popOverContainer,
|
||||
quickEditFormRef,
|
||||
onImageEnlarge
|
||||
}: CellProps) {
|
||||
if (column.name && item.rowSpans[column.name] === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [style, stickyClassName]: any = React.useMemo(() => {
|
||||
const style = {...column.pristine.style};
|
||||
const [stickyStyle, stickyClassName] = store.getStickyStyles(
|
||||
column,
|
||||
store.filteredColumns
|
||||
);
|
||||
return [Object.assign(style, stickyStyle), stickyClassName];
|
||||
}, []);
|
||||
|
||||
const onCheckboxChange = React.useCallback(() => {
|
||||
onCheck?.(item);
|
||||
}, []);
|
||||
|
||||
if (column.type === '__checkme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
<Checkbox
|
||||
classPrefix={ns}
|
||||
type={multiple ? 'checkbox' : 'radio'}
|
||||
checked={item.checked}
|
||||
disabled={item.checkdisable || !item.checkable}
|
||||
onChange={onCheckboxChange}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
} else if (column.type === '__dragme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName, {
|
||||
'is-dragDisabled': !item.draggable
|
||||
})}
|
||||
>
|
||||
{item.draggable ? <Icon icon="drag" className="icon" /> : null}
|
||||
</td>
|
||||
);
|
||||
} else if (column.type === '__expandme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
{item.expandable ? (
|
||||
<a
|
||||
className={cx('Table-expandBtn', item.expanded ? 'is-active' : '')}
|
||||
// data-tooltip="展开/收起"
|
||||
// data-position="top"
|
||||
onClick={item.toggleExpanded}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
let [prefix, affix, addtionalClassName] = React.useMemo(() => {
|
||||
let prefix: React.ReactNode[] = [];
|
||||
let affix: React.ReactNode[] = [];
|
||||
let addtionalClassName = '';
|
||||
|
||||
if (column.isPrimary && store.isNested) {
|
||||
addtionalClassName = 'Table-primayCell';
|
||||
prefix.push(
|
||||
<span
|
||||
key="indent"
|
||||
className={cx('Table-indent')}
|
||||
style={item.indentStyle}
|
||||
/>
|
||||
);
|
||||
prefix.push(
|
||||
item.expandable ? (
|
||||
<a
|
||||
key="expandBtn2"
|
||||
className={cx('Table-expandBtn2', item.expanded ? 'is-active' : '')}
|
||||
// data-tooltip="展开/收起"
|
||||
// data-position="top"
|
||||
onClick={item.toggleExpanded}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
) : (
|
||||
<span key="expandSpace" className={cx('Table-expandSpace')} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!ignoreDrag &&
|
||||
column.isPrimary &&
|
||||
store.isNested &&
|
||||
store.draggable &&
|
||||
item.draggable
|
||||
) {
|
||||
affix.push(
|
||||
<a
|
||||
key="dragBtn"
|
||||
draggable
|
||||
onDragStart={onDragStart}
|
||||
className={cx('Table-dragBtn')}
|
||||
>
|
||||
<Icon icon="drag" className="icon" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return [prefix, affix, addtionalClassName];
|
||||
}, [item.expandable, item.expanded, column.isPrimary]);
|
||||
|
||||
// 根据条件缓存 data,避免孩子重复渲染
|
||||
const hasCustomTrackExpression =
|
||||
typeof column.pristine.trackExpression !== 'undefined';
|
||||
const trackExpression = hasCustomTrackExpression
|
||||
? column.pristine.trackExpression
|
||||
: React.useMemo(() => buildTrackExpression(column.pristine), []);
|
||||
const data = React.useMemo(
|
||||
() => item.locals,
|
||||
[
|
||||
hasCustomTrackExpression ? '' : JSON.stringify(item.locals),
|
||||
evalTrackExpression(trackExpression, item.locals)
|
||||
]
|
||||
);
|
||||
|
||||
const finalCanAccessSuperData =
|
||||
column.pristine.canAccessSuperData ?? canAccessSuperData;
|
||||
const subProps: any = {
|
||||
...props,
|
||||
// 操作列不下发loading,否则会导致操作栏里面的所有按钮都出现loading
|
||||
loading: column.type === 'operation' ? false : props.loading,
|
||||
btnDisabled: store.dragging,
|
||||
data: data,
|
||||
value: column.name
|
||||
? resolveVariable(
|
||||
column.name,
|
||||
finalCanAccessSuperData ? item.locals : item.data
|
||||
)
|
||||
: column.value,
|
||||
popOverContainer: popOverContainer,
|
||||
rowSpan: item.rowSpans[column.name as string],
|
||||
quickEditFormRef: quickEditFormRef,
|
||||
cellPrefix: prefix,
|
||||
cellAffix: affix,
|
||||
onImageEnlarge: onImageEnlarge,
|
||||
canAccessSuperData: finalCanAccessSuperData,
|
||||
row: item,
|
||||
itemBadge,
|
||||
showBadge:
|
||||
!props.isHead &&
|
||||
itemBadge &&
|
||||
store.firstToggledColumnIndex === props.colIndex,
|
||||
onQuery: undefined,
|
||||
style,
|
||||
className: cx(
|
||||
column.pristine.className,
|
||||
stickyClassName,
|
||||
addtionalClassName
|
||||
),
|
||||
/** 给子节点的设置默认值,避免取到env.affixHeader的默认值,导致表头覆盖首行 */
|
||||
affixOffsetTop: 0
|
||||
};
|
||||
delete subProps.label;
|
||||
|
||||
return render(
|
||||
region,
|
||||
{
|
||||
...column.pristine,
|
||||
column: column.pristine,
|
||||
type: 'cell'
|
||||
},
|
||||
subProps
|
||||
);
|
||||
}
|
@ -9,17 +9,45 @@ export function ColGroup({
|
||||
columns: Array<IColumn>;
|
||||
store: ITableStore;
|
||||
}) {
|
||||
const domRef = React.createRef<HTMLTableColElement>();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (domRef.current) {
|
||||
store.initTableWidth();
|
||||
store.syncTableWidth();
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const table = domRef.current!.parentElement!;
|
||||
const observer = new MutationObserver(() => {
|
||||
store.syncTableWidth();
|
||||
});
|
||||
observer.observe(table, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<colgroup>
|
||||
<colgroup ref={domRef}>
|
||||
{columns.map(column => {
|
||||
const style: any = {};
|
||||
|
||||
if (store.columnWidthReady) {
|
||||
if (store.columnWidthReady && column.width) {
|
||||
style.width = column.width;
|
||||
} else if (column.pristine.width) {
|
||||
style.width = column.pristine.width;
|
||||
}
|
||||
|
||||
if (store.tableLayout === 'auto' && style.width) {
|
||||
style.minWidth = style.width;
|
||||
}
|
||||
|
||||
return <col data-index={column.index} style={style} key={column.id} />;
|
||||
})}
|
||||
</colgroup>
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {ClassNamesFn, RendererEvent} from 'amis-core';
|
||||
|
||||
import {SchemaNode, ActionObject} from 'amis-core';
|
||||
import {TableRow} from './TableRow';
|
||||
import TableRow from './TableRow';
|
||||
import {filter} from 'amis-core';
|
||||
import {observer} from 'mobx-react';
|
||||
import {trace, reaction} from 'mobx';
|
||||
@ -65,7 +65,7 @@ export interface TableBodyProps extends LocaleProps {
|
||||
@observer
|
||||
export class TableBody extends React.Component<TableBodyProps> {
|
||||
componentDidMount(): void {
|
||||
this.props.store.syncTableWidth();
|
||||
this.props.store.initTableWidth();
|
||||
}
|
||||
|
||||
renderRows(
|
||||
@ -91,7 +91,8 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
onRowClick,
|
||||
onRowDbClick,
|
||||
onRowMouseEnter,
|
||||
onRowMouseLeave
|
||||
onRowMouseLeave,
|
||||
store
|
||||
} = this.props;
|
||||
|
||||
return rows.map((item: IRow, rowIndex: number) => {
|
||||
@ -99,6 +100,7 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
const doms = [
|
||||
<TableRow
|
||||
{...itemProps}
|
||||
store={store}
|
||||
itemAction={itemAction}
|
||||
classnames={cx}
|
||||
checkOnItemClick={checkOnItemClick}
|
||||
@ -136,6 +138,7 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
doms.push(
|
||||
<TableRow
|
||||
{...itemProps}
|
||||
store={store}
|
||||
itemAction={itemAction}
|
||||
classnames={cx}
|
||||
checkOnItemClick={checkOnItemClick}
|
||||
|
@ -81,53 +81,60 @@ export interface TableContentProps extends LocaleProps {
|
||||
dispatchEvent?: Function;
|
||||
onEvent?: OnEventProps;
|
||||
loading?: boolean;
|
||||
columnWidthReady?: boolean;
|
||||
|
||||
// 以下纯粹是为了监控
|
||||
someChecked?: boolean;
|
||||
allChecked?: boolean;
|
||||
isSelectionThresholdReached?: boolean;
|
||||
orderBy?: string;
|
||||
orderDir?: string;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class TableContent extends React.Component<TableContentProps> {
|
||||
static renderItemActions(
|
||||
props: Pick<
|
||||
TableContentProps,
|
||||
'itemActions' | 'render' | 'store' | 'classnames'
|
||||
>
|
||||
) {
|
||||
const {itemActions, render, store, classnames: cx} = props;
|
||||
export function renderItemActions(
|
||||
props: Pick<
|
||||
TableContentProps,
|
||||
'itemActions' | 'render' | 'store' | 'classnames'
|
||||
>
|
||||
) {
|
||||
const {itemActions, render, store, classnames: cx} = props;
|
||||
|
||||
if (!store.hoverRow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const finalActions = Array.isArray(itemActions)
|
||||
? itemActions.filter(action => !action.hiddenOnHover)
|
||||
: [];
|
||||
|
||||
if (!finalActions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ItemActionsWrapper store={store} classnames={cx}>
|
||||
<div className={cx('Table-itemActions')}>
|
||||
{finalActions.map((action, index) =>
|
||||
render(
|
||||
`itemAction/${index}`,
|
||||
{
|
||||
...(action as any),
|
||||
isMenuItem: true
|
||||
},
|
||||
{
|
||||
key: index,
|
||||
item: store.hoverRow,
|
||||
data: store.hoverRow!.locals,
|
||||
rowIndex: store.hoverRow!.index
|
||||
}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</ItemActionsWrapper>
|
||||
);
|
||||
if (!store.hoverRow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const finalActions = Array.isArray(itemActions)
|
||||
? itemActions.filter(action => !action.hiddenOnHover)
|
||||
: [];
|
||||
|
||||
if (!finalActions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ItemActionsWrapper store={store} classnames={cx}>
|
||||
<div className={cx('Table-itemActions')}>
|
||||
{finalActions.map((action, index) =>
|
||||
render(
|
||||
`itemAction/${index}`,
|
||||
{
|
||||
...(action as any),
|
||||
isMenuItem: true
|
||||
},
|
||||
{
|
||||
key: index,
|
||||
item: store.hoverRow,
|
||||
data: store.hoverRow!.locals,
|
||||
rowIndex: store.hoverRow!.index
|
||||
}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</ItemActionsWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export class TableContent extends React.PureComponent<TableContentProps> {
|
||||
render() {
|
||||
const {
|
||||
placeholder,
|
||||
@ -182,7 +189,7 @@ export class TableContent extends React.Component<TableContentProps> {
|
||||
ref={tableRef}
|
||||
className={cx(
|
||||
tableClassName,
|
||||
store.columnWidthReady ? 'is-layout-fixed' : undefined
|
||||
store.tableLayout === 'fixed' ? 'is-layout-fixed' : undefined
|
||||
)}
|
||||
>
|
||||
<ColGroup columns={columns} store={store} />
|
||||
@ -293,7 +300,6 @@ export class TableContent extends React.Component<TableContentProps> {
|
||||
affixRow={affixRow}
|
||||
data={data}
|
||||
rowsProps={{
|
||||
data,
|
||||
dispatchEvent,
|
||||
onEvent
|
||||
}}
|
||||
@ -304,3 +310,27 @@ export class TableContent extends React.Component<TableContentProps> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default observer((props: TableContentProps) => {
|
||||
const store = props.store;
|
||||
|
||||
// 分析 table/index.tsx 中的 renderHeadCell 依赖了以下属性
|
||||
// store.someChecked;
|
||||
// store.allChecked;
|
||||
// store.isSelectionThresholdReached;
|
||||
// store.allExpanded;
|
||||
// store.orderBy
|
||||
// store.orderDir
|
||||
|
||||
return (
|
||||
<TableContent
|
||||
{...props}
|
||||
columnWidthReady={store.columnWidthReady}
|
||||
someChecked={store.someChecked}
|
||||
allChecked={store.allChecked}
|
||||
isSelectionThresholdReached={store.isSelectionThresholdReached}
|
||||
orderBy={store.orderBy}
|
||||
orderDir={store.orderDir}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,11 +1,19 @@
|
||||
import {observer} from 'mobx-react';
|
||||
import React from 'react';
|
||||
import type {IColumn, IRow} from 'amis-core/lib/store/table';
|
||||
import {RendererEvent, RendererProps} from 'amis-core';
|
||||
import {
|
||||
ITableStore,
|
||||
RendererEvent,
|
||||
RendererProps,
|
||||
autobind,
|
||||
traceProps
|
||||
} from 'amis-core';
|
||||
import {Action} from '../Action';
|
||||
import {isClickOnInput, createObject} from 'amis-core';
|
||||
import {isClickOnInput} from 'amis-core';
|
||||
import {useInView} from 'react-intersection-observer';
|
||||
|
||||
interface TableRowProps extends Pick<RendererProps, 'render'> {
|
||||
store: ITableStore;
|
||||
onCheck: (item: IRow, value: boolean, shift?: boolean) => Promise<void>;
|
||||
onRowClick: (item: IRow, index: number) => Promise<RendererEvent<any> | void>;
|
||||
onRowDbClick: (
|
||||
@ -38,31 +46,39 @@ interface TableRowProps extends Pick<RendererProps, 'render'> {
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class TableRow extends React.Component<TableRowProps> {
|
||||
// reaction?: () => void;
|
||||
constructor(props: TableRowProps) {
|
||||
super(props);
|
||||
this.handleAction = this.handleAction.bind(this);
|
||||
this.handleQuickChange = this.handleQuickChange.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleItemClick = this.handleItemClick.bind(this);
|
||||
this.handleDbClick = this.handleDbClick.bind(this);
|
||||
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
||||
export class TableRow extends React.PureComponent<
|
||||
TableRowProps & {
|
||||
// 这些属性纯粹是为了监控变化,不要在 render 里面使用
|
||||
expanded: boolean;
|
||||
parentExpanded?: boolean;
|
||||
id: string;
|
||||
newIndex: number;
|
||||
isHover: boolean;
|
||||
checked: boolean;
|
||||
modified: boolean;
|
||||
moved: boolean;
|
||||
depth: number;
|
||||
expandable: boolean;
|
||||
appeard?: boolean;
|
||||
checkdisable: boolean;
|
||||
trRef?: React.Ref<any>;
|
||||
isNested?: boolean;
|
||||
}
|
||||
|
||||
> {
|
||||
@autobind
|
||||
handleMouseEnter(e: React.MouseEvent<HTMLTableRowElement>) {
|
||||
const {item, itemIndex, onRowMouseEnter} = this.props;
|
||||
onRowMouseEnter?.(item?.data, itemIndex);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleMouseLeave(e: React.MouseEvent<HTMLTableRowElement>) {
|
||||
const {item, itemIndex, onRowMouseLeave} = this.props;
|
||||
onRowMouseLeave?.(item?.data, itemIndex);
|
||||
}
|
||||
|
||||
// 定义点击一行的行为,通过 itemAction配置
|
||||
@autobind
|
||||
async handleItemClick(e: React.MouseEvent<HTMLTableRowElement>) {
|
||||
if (isClickOnInput(e)) {
|
||||
return;
|
||||
@ -92,16 +108,19 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleDbClick(e: React.MouseEvent<HTMLTableRowElement>) {
|
||||
const {item, itemIndex, onRowDbClick} = this.props;
|
||||
onRowDbClick?.(item?.data, itemIndex);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleAction(e: React.UIEvent<any>, action: Action, ctx: any) {
|
||||
const {onAction, item} = this.props;
|
||||
onAction && onAction(e, action, ctx || item.locals);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleQuickChange(
|
||||
values: object,
|
||||
saveImmediately?: boolean,
|
||||
@ -116,6 +135,7 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
onQuickChange(item, values, saveImmediately, savePristine, options);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChange(
|
||||
value: any,
|
||||
name: string,
|
||||
@ -157,18 +177,35 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
parent,
|
||||
itemAction,
|
||||
onEvent,
|
||||
|
||||
expanded,
|
||||
parentExpanded,
|
||||
id,
|
||||
newIndex,
|
||||
isHover,
|
||||
checked,
|
||||
modified,
|
||||
moved,
|
||||
depth,
|
||||
expandable,
|
||||
appeard,
|
||||
checkdisable,
|
||||
trRef,
|
||||
isNested,
|
||||
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
if (footableMode) {
|
||||
if (!item.expanded) {
|
||||
if (!expanded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr
|
||||
data-id={item.id}
|
||||
data-index={item.newIndex}
|
||||
ref={trRef}
|
||||
data-id={id}
|
||||
data-index={newIndex}
|
||||
onClick={
|
||||
checkOnItemClick || itemAction || onEvent?.rowClick
|
||||
? this.handleItemClick
|
||||
@ -178,10 +215,10 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
className={cx(itemClassName, {
|
||||
'is-hovered': item.isHover,
|
||||
'is-checked': item.checked,
|
||||
'is-modified': item.modified,
|
||||
'is-moved': item.moved,
|
||||
'is-hovered': isHover,
|
||||
'is-checked': checked,
|
||||
'is-modified': modified,
|
||||
'is-moved': moved,
|
||||
[`Table-tr--hasItemAction`]: itemAction, // 就是为了加鼠标效果
|
||||
[`Table-tr--odd`]: itemIndex % 2 === 0,
|
||||
[`Table-tr--even`]: itemIndex % 2 === 1
|
||||
@ -208,20 +245,26 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
</th>
|
||||
) : null}
|
||||
|
||||
{renderCell(
|
||||
`${regionPrefix}${itemIndex}/${column.index}`,
|
||||
column,
|
||||
item,
|
||||
{
|
||||
...rest,
|
||||
width: null,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
key: column.index,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
onChange: this.handleChange
|
||||
}
|
||||
{appeard ? (
|
||||
renderCell(
|
||||
`${regionPrefix}${itemIndex}/${column.index}`,
|
||||
column,
|
||||
item,
|
||||
{
|
||||
...rest,
|
||||
width: null,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
key: column.index,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
onChange: this.handleChange
|
||||
}
|
||||
)
|
||||
) : (
|
||||
<td key={column.index}>
|
||||
<div className={cx('Table-emptyBlock')}> </div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
@ -238,6 +281,7 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
|
||||
return (
|
||||
<tr
|
||||
ref={trRef}
|
||||
onClick={
|
||||
checkOnItemClick || itemAction || onEvent?.rowClick
|
||||
? this.handleItemClick
|
||||
@ -246,36 +290,82 @@ export class TableRow extends React.Component<TableRowProps> {
|
||||
onDoubleClick={this.handleDbClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
data-index={item.depth === 1 ? item.newIndex : undefined}
|
||||
data-id={item.id}
|
||||
data-index={depth === 1 ? newIndex : undefined}
|
||||
data-id={id}
|
||||
className={cx(
|
||||
itemClassName,
|
||||
{
|
||||
'is-hovered': item.isHover,
|
||||
'is-checked': item.checked,
|
||||
'is-modified': item.modified,
|
||||
'is-moved': item.moved,
|
||||
'is-expanded': item.expanded && item.expandable,
|
||||
'is-expandable': item.expandable,
|
||||
'is-hovered': isHover,
|
||||
'is-checked': checked,
|
||||
'is-modified': modified,
|
||||
'is-moved': moved,
|
||||
'is-expanded': expanded && expandable,
|
||||
'is-expandable': expandable,
|
||||
[`Table-tr--hasItemAction`]: itemAction,
|
||||
[`Table-tr--odd`]: itemIndex % 2 === 0,
|
||||
[`Table-tr--even`]: itemIndex % 2 === 1
|
||||
},
|
||||
`Table-tr--${item.depth}th`
|
||||
`Table-tr--${depth}th`
|
||||
)}
|
||||
>
|
||||
{columns.map(column =>
|
||||
renderCell(`${itemIndex}/${column.index}`, column, item, {
|
||||
...rest,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
key: column.index,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
onChange: this.handleChange
|
||||
})
|
||||
appeard ? (
|
||||
renderCell(`${itemIndex}/${column.index}`, column, item, {
|
||||
...rest,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
key: column.id,
|
||||
onAction: this.handleAction,
|
||||
onQuickChange: this.handleQuickChange,
|
||||
onChange: this.handleChange
|
||||
})
|
||||
) : (
|
||||
<td key={column.id}>
|
||||
<div className={cx('Table-emptyBlock')}> </div>
|
||||
</td>
|
||||
)
|
||||
)}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 换成 mobx-react-lite 模式
|
||||
export default observer((props: TableRowProps) => {
|
||||
const item = props.item;
|
||||
const parent = props.parent;
|
||||
const store = props.store;
|
||||
const columns = props.columns;
|
||||
const canAccessSuperData =
|
||||
store.canAccessSuperData ||
|
||||
columns.some(item => item.pristine.canAccessSuperData);
|
||||
|
||||
const {ref, inView} = useInView({
|
||||
threshold: 0,
|
||||
onChange: item.markAppeared,
|
||||
skip: !item.lazyRender
|
||||
});
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
{...props}
|
||||
trRef={ref}
|
||||
expanded={item.expanded}
|
||||
parentExpanded={parent?.expanded}
|
||||
id={item.id}
|
||||
newIndex={item.newIndex}
|
||||
isHover={item.isHover}
|
||||
checked={item.checked}
|
||||
modified={item.modified}
|
||||
moved={item.moved}
|
||||
depth={item.depth}
|
||||
expandable={item.expandable}
|
||||
checkdisable={item.checkdisable}
|
||||
// data 在 TableRow 里面没有使用,这里写上是为了当列数据变化的时候 TableRow 重新渲染,
|
||||
// 不是 item.locals 的原因是 item.locals 会变化多次,比如父级上下文变化也会进来,但是 item.data 只会变化一次。
|
||||
data={canAccessSuperData ? item.locals : item.data}
|
||||
appeard={item.lazyRender ? item.appeared || inView : true}
|
||||
isNested={store.isNested}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -17,6 +17,7 @@ import {Button} from 'amis-ui';
|
||||
import {TableStore, ITableStore, padArr} from 'amis-core';
|
||||
import {
|
||||
anyChanged,
|
||||
changedEffect,
|
||||
getScrollParent,
|
||||
difference,
|
||||
autobind,
|
||||
@ -38,7 +39,7 @@ import {TableCell} from './TableCell';
|
||||
import type {AutoGenerateFilterObject} from '../CRUD';
|
||||
import {HeadCellFilterDropDown} from './HeadCellFilterDropdown';
|
||||
import {HeadCellSearchDropDown} from './HeadCellSearchDropdown';
|
||||
import {TableContent} from './TableContent';
|
||||
import TableContent, {renderItemActions} from './TableContent';
|
||||
import {
|
||||
BaseSchema,
|
||||
SchemaApi,
|
||||
@ -67,6 +68,8 @@ import isPlainObject from 'lodash/isPlainObject';
|
||||
import omit from 'lodash/omit';
|
||||
import ColGroup from './ColGroup';
|
||||
import debounce from 'lodash/debounce';
|
||||
import AutoFilterForm from './AutoFilterForm';
|
||||
import Cell from './Cell';
|
||||
|
||||
/**
|
||||
* 表格列,不指定类型时默认为文本类型。
|
||||
@ -183,6 +186,12 @@ export type TableColumnObject = {
|
||||
*/
|
||||
canAccessSuperData?: boolean;
|
||||
|
||||
/**
|
||||
* 当一次性渲染太多列上有用,默认为 100,可以用来提升表格渲染性能
|
||||
* @default 100
|
||||
*/
|
||||
lazyRenderAfter?: number;
|
||||
|
||||
/**
|
||||
* 单元格内部组件自定义样式 style作为单元格自定义样式的配置
|
||||
*/
|
||||
@ -328,6 +337,11 @@ export interface TableSchema extends BaseSchema {
|
||||
* 表格自动计算高度
|
||||
*/
|
||||
autoFillHeight?: boolean | AutoFillHeightObject;
|
||||
|
||||
/**
|
||||
* table layout
|
||||
*/
|
||||
tableLayout?: 'fixed' | 'auto';
|
||||
}
|
||||
|
||||
export interface TableProps extends RendererProps, SpinnerExtraProps {
|
||||
@ -515,6 +529,15 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
leading: false
|
||||
});
|
||||
|
||||
updateAutoFillHeightLazy = debounce(
|
||||
this.updateAutoFillHeight.bind(this),
|
||||
250,
|
||||
{
|
||||
trailing: true,
|
||||
leading: false
|
||||
}
|
||||
);
|
||||
|
||||
constructor(props: TableProps, context: IScopedContext) {
|
||||
super(props);
|
||||
|
||||
@ -525,7 +548,6 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
this.tableRef = this.tableRef.bind(this);
|
||||
this.affixedTableRef = this.affixedTableRef.bind(this);
|
||||
this.updateTableInfo = this.updateTableInfo.bind(this);
|
||||
this.updateTableInfoRef = this.updateTableInfoRef.bind(this);
|
||||
this.handleAction = this.handleAction.bind(this);
|
||||
this.handleCheck = this.handleCheck.bind(this);
|
||||
this.handleCheckAll = this.handleCheckAll.bind(this);
|
||||
@ -569,7 +591,11 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength,
|
||||
onQuery,
|
||||
autoGenerateFilter
|
||||
autoGenerateFilter,
|
||||
loading,
|
||||
canAccessSuperData,
|
||||
lazyRenderAfter,
|
||||
tableLayout
|
||||
} = props;
|
||||
|
||||
let combineNum = props.combineNum;
|
||||
@ -597,7 +623,11 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
combineNum,
|
||||
combineFromIndex,
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength
|
||||
maxKeepItemSelectionLength,
|
||||
loading,
|
||||
canAccessSuperData,
|
||||
lazyRenderAfter,
|
||||
tableLayout
|
||||
});
|
||||
|
||||
if (
|
||||
@ -652,17 +682,19 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
const currentNode = findDOMNode(this) as HTMLElement;
|
||||
|
||||
if (this.props.autoFillHeight) {
|
||||
let frame: number;
|
||||
let fn = () => {
|
||||
cancelAnimationFrame(frame);
|
||||
frame = requestAnimationFrame(this.updateAutoFillHeight);
|
||||
};
|
||||
this.toDispose.push(resizeSensor(currentNode.parentElement!, fn));
|
||||
this.toDispose.push(
|
||||
resizeSensor(
|
||||
currentNode.parentElement!,
|
||||
this.updateAutoFillHeightLazy,
|
||||
false,
|
||||
'height'
|
||||
)
|
||||
);
|
||||
this.updateAutoFillHeight();
|
||||
}
|
||||
|
||||
this.toDispose.push(
|
||||
resizeSensor(currentNode.parentElement!, this.updateTableInfoLazy)
|
||||
resizeSensor(currentNode, this.updateTableInfoLazy, false, 'width')
|
||||
);
|
||||
const {store, autoGenerateFilter, onSearchableFromInit} = this.props;
|
||||
|
||||
@ -761,7 +793,9 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
|
||||
const tableContentHeight = heightValue
|
||||
? `${heightValue}px`
|
||||
: `${viewportHeight - tableContentTop - tableContentBottom}px`;
|
||||
: `${Math.round(
|
||||
viewportHeight - tableContentTop - tableContentBottom
|
||||
)}px`;
|
||||
|
||||
tableContent.style[heightField] = tableContentHeight;
|
||||
}
|
||||
@ -770,58 +804,50 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
const props = this.props;
|
||||
const store = props.store;
|
||||
|
||||
if (
|
||||
anyChanged(
|
||||
[
|
||||
'selectable',
|
||||
'columnsTogglable',
|
||||
'draggable',
|
||||
'orderBy',
|
||||
'orderDir',
|
||||
'multiple',
|
||||
'footable',
|
||||
'primaryField',
|
||||
'itemCheckableOn',
|
||||
'itemDraggableOn',
|
||||
'hideCheckToggler',
|
||||
'combineNum',
|
||||
'combineFromIndex',
|
||||
'expandConfig'
|
||||
],
|
||||
prevProps,
|
||||
props
|
||||
)
|
||||
) {
|
||||
let combineNum = props.combineNum;
|
||||
if (typeof combineNum === 'string') {
|
||||
combineNum = parseInt(
|
||||
resolveVariableAndFilter(combineNum, props.data, '| raw'),
|
||||
10
|
||||
);
|
||||
changedEffect(
|
||||
[
|
||||
'selectable',
|
||||
'columnsTogglable',
|
||||
'draggable',
|
||||
'orderBy',
|
||||
'orderDir',
|
||||
'multiple',
|
||||
'footable',
|
||||
'primaryField',
|
||||
'itemCheckableOn',
|
||||
'itemDraggableOn',
|
||||
'hideCheckToggler',
|
||||
'combineNum',
|
||||
'combineFromIndex',
|
||||
'expandConfig',
|
||||
'columns',
|
||||
'loading',
|
||||
'canAccessSuperData',
|
||||
'lazyRenderAfter',
|
||||
'tableLayout'
|
||||
],
|
||||
prevProps,
|
||||
props,
|
||||
changes => {
|
||||
if (
|
||||
changes.hasOwnProperty('combineNum') &&
|
||||
typeof changes.combineNum === 'string'
|
||||
) {
|
||||
changes.combineNum = parseInt(
|
||||
resolveVariableAndFilter(
|
||||
changes.combineNum as string,
|
||||
props.data,
|
||||
'| raw'
|
||||
),
|
||||
10
|
||||
);
|
||||
}
|
||||
if (changes.orderBy && !props.onQuery) {
|
||||
delete changes.orderBy;
|
||||
}
|
||||
store.update(changes as any);
|
||||
}
|
||||
store.update({
|
||||
selectable: props.selectable,
|
||||
columnsTogglable: props.columnsTogglable,
|
||||
draggable: props.draggable,
|
||||
orderBy: props.onQuery ? props.orderBy : undefined,
|
||||
orderDir: props.orderDir,
|
||||
multiple: props.multiple,
|
||||
primaryField: props.primaryField,
|
||||
footable: props.footable,
|
||||
itemCheckableOn: props.itemCheckableOn,
|
||||
itemDraggableOn: props.itemDraggableOn,
|
||||
hideCheckToggler: props.hideCheckToggler,
|
||||
combineNum: combineNum,
|
||||
combineFromIndex: props.combineFromIndex,
|
||||
expandConfig: props.expandConfig
|
||||
});
|
||||
}
|
||||
|
||||
if (prevProps.columns !== props.columns) {
|
||||
store.update({
|
||||
columns: props.columns
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
anyChanged(['source', 'value', 'items'], prevProps, props) ||
|
||||
@ -849,6 +875,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
this.toDispose = [];
|
||||
|
||||
this.updateTableInfoLazy.cancel();
|
||||
this.updateAutoFillHeightLazy.cancel();
|
||||
formItem && isAlive(formItem) && formItem.setSubStore(null);
|
||||
clearTimeout(this.timer);
|
||||
|
||||
@ -1091,6 +1118,27 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
}
|
||||
}
|
||||
|
||||
// 校验直接放在单元格里面的表单项
|
||||
const subFormItems = store.children.filter(
|
||||
item => item?.storeType === 'FormItemStore'
|
||||
);
|
||||
if (subFormItems.length) {
|
||||
const result = await Promise.all(
|
||||
subFormItems.map(item => {
|
||||
let ctx = {};
|
||||
|
||||
if (item.rowIndex && store.rows[item.rowIndex]) {
|
||||
ctx = store.rows[item.rowIndex].data;
|
||||
}
|
||||
|
||||
return item.validate(ctx);
|
||||
})
|
||||
);
|
||||
if (~result.indexOf(false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const rows = store.modifiedRows.map(item => item.data);
|
||||
const rowIndexes = store.modifiedRows.map(item => item.path);
|
||||
const diff = store.modifiedRows.map(item =>
|
||||
@ -1149,6 +1197,14 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
key => this.subForms[key] && subForms.push(this.subForms[key])
|
||||
);
|
||||
subForms.forEach(item => item.clearErrors());
|
||||
|
||||
// 去掉错误提示
|
||||
const subFormItems = store.children.filter(
|
||||
item => item?.storeType === 'FormItemStore'
|
||||
);
|
||||
if (subFormItems.length) {
|
||||
subFormItems.map(item => item.reset());
|
||||
}
|
||||
}
|
||||
|
||||
bulkUpdate(value: any, items: Array<object>) {
|
||||
@ -1177,18 +1233,11 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
if (this.resizeLine) {
|
||||
return;
|
||||
}
|
||||
this.props.store.syncTableWidth();
|
||||
this.props.store.initTableWidth();
|
||||
this.handleOutterScroll();
|
||||
callback && setTimeout(callback, 20);
|
||||
}
|
||||
|
||||
updateTableInfoRef(ref: any) {
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
this.updateTableInfo();
|
||||
}
|
||||
|
||||
// 当表格滚动是,需要让 affixHeader 部分的表格也滚动
|
||||
handleOutterScroll() {
|
||||
const table = this.table as HTMLElement;
|
||||
@ -1504,7 +1553,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
const store = this.props.store;
|
||||
const index = parseInt(this.resizeLine!.getAttribute('data-index')!, 10);
|
||||
const column = store.columns[index];
|
||||
this.lineStartWidth = column.width;
|
||||
this.lineStartWidth = column.realWidth || column.width;
|
||||
this.resizeLine!.classList.add('is-resizing');
|
||||
|
||||
document.addEventListener('mousemove', this.handleColResizeMouseMove);
|
||||
@ -1545,165 +1594,29 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
onSearchableFromSubmit,
|
||||
onSearchableFromInit,
|
||||
classnames: cx,
|
||||
autoGenerateFilter,
|
||||
translate: __,
|
||||
query,
|
||||
data
|
||||
data,
|
||||
autoGenerateFilter
|
||||
} = this.props;
|
||||
const {columnsNum, showBtnToolbar} =
|
||||
typeof autoGenerateFilter === 'boolean'
|
||||
? {
|
||||
columnsNum: 3,
|
||||
showBtnToolbar: true
|
||||
}
|
||||
: autoGenerateFilter;
|
||||
const searchableColumns = store.searchableColumns;
|
||||
const activedSearchableColumns = store.activedSearchableColumns;
|
||||
|
||||
const searchableColumns = store.searchableColumns;
|
||||
if (!searchableColumns.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const body: Array<any> = padArr(activedSearchableColumns, columnsNum).map(
|
||||
group => ({
|
||||
type: 'group',
|
||||
body: group.map((column: any) => ({
|
||||
...(column.searchable === true
|
||||
? {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
label: column.label
|
||||
}
|
||||
: {
|
||||
type: 'input-text',
|
||||
name: column.name,
|
||||
...column.searchable
|
||||
}),
|
||||
name: column.searchable?.name ?? column.name,
|
||||
label: column.searchable?.label ?? column.label
|
||||
}))
|
||||
})
|
||||
);
|
||||
|
||||
let showExpander = searchableColumns.length >= columnsNum;
|
||||
|
||||
// todo 以后做动画
|
||||
if (!store.searchFormExpanded && body.length) {
|
||||
body.splice(1, body.length - 1);
|
||||
body[0].body.splice(columnsNum - 1, body[0].body.length - columnsNum + 1);
|
||||
}
|
||||
|
||||
let lastGroup = body[body.length - 1];
|
||||
if (
|
||||
!Array.isArray(lastGroup?.body) ||
|
||||
lastGroup.body.length >= columnsNum
|
||||
) {
|
||||
lastGroup = {
|
||||
type: 'group',
|
||||
body: []
|
||||
};
|
||||
body.push(lastGroup);
|
||||
}
|
||||
|
||||
let count = Math.max(columnsNum - lastGroup.body.length - 1);
|
||||
while (count-- > 0) {
|
||||
lastGroup.body.push({
|
||||
type: 'tpl',
|
||||
tpl: ''
|
||||
});
|
||||
}
|
||||
lastGroup.body.push({
|
||||
type: 'container',
|
||||
className: 'ButtonToolbar text-right block',
|
||||
wrapperBody: false,
|
||||
body: [
|
||||
{
|
||||
type: 'dropdown-button',
|
||||
label: __('Table.searchFields'),
|
||||
className: cx('Table-searchableForm-dropdown', 'mr-2'),
|
||||
level: 'link',
|
||||
trigger: 'click',
|
||||
size: 'sm',
|
||||
align: 'right',
|
||||
visible: showBtnToolbar,
|
||||
buttons: searchableColumns.map(column => {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
label: false,
|
||||
className: cx('Table-searchableForm-checkbox'),
|
||||
inputClassName: cx('Table-searchableForm-checkbox-inner'),
|
||||
name: `__search_${column.searchable?.name ?? column.name}`,
|
||||
option: column.searchable?.label ?? column.label,
|
||||
value: column.enableSearch,
|
||||
badge: {
|
||||
offset: [-10, 5],
|
||||
visibleOn: `${
|
||||
column.toggable && !column.toggled && column.enableSearch
|
||||
}`
|
||||
},
|
||||
onChange: (value: boolean) => {
|
||||
column.setEnableSearch(value);
|
||||
store.setSearchFormExpanded(true);
|
||||
}
|
||||
};
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
type: 'submit',
|
||||
label: __('search'),
|
||||
level: 'primary',
|
||||
className: 'w-18'
|
||||
},
|
||||
{
|
||||
type: 'reset',
|
||||
label: __('reset'),
|
||||
className: 'w-18'
|
||||
},
|
||||
|
||||
showExpander
|
||||
? {
|
||||
children: () => (
|
||||
<a
|
||||
className={cx(
|
||||
'Table-SFToggler',
|
||||
store.searchFormExpanded ? 'is-expanded' : ''
|
||||
)}
|
||||
onClick={store.toggleSearchFormExpanded}
|
||||
>
|
||||
{__(store.searchFormExpanded ? 'collapse' : 'expand')}
|
||||
<span className={cx('Table-SFToggler-arrow')}>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
: null
|
||||
].filter(item => item)
|
||||
});
|
||||
|
||||
return render(
|
||||
'searchable-form',
|
||||
{
|
||||
type: 'form',
|
||||
api: null,
|
||||
title: '',
|
||||
mode: 'horizontal',
|
||||
submitText: __('search'),
|
||||
body: body,
|
||||
actions: [],
|
||||
canAccessSuperData: false
|
||||
},
|
||||
{
|
||||
key: 'searchable-form',
|
||||
panelClassName: cx('Table-searchableForm'),
|
||||
actionsClassName: cx('Table-searchableForm-footer'),
|
||||
onReset: onSearchableFromReset,
|
||||
onSubmit: onSearchableFromSubmit,
|
||||
onInit: onSearchableFromInit,
|
||||
formStore: undefined,
|
||||
data: query ? createObject(data, query) : data
|
||||
}
|
||||
return (
|
||||
<AutoFilterForm
|
||||
store={store}
|
||||
query={query}
|
||||
data={data}
|
||||
translate={__}
|
||||
classnames={cx}
|
||||
render={render}
|
||||
autoGenerateFilter={autoGenerateFilter}
|
||||
onSearchableFromReset={onSearchableFromReset}
|
||||
onSearchableFromSubmit={onSearchableFromSubmit}
|
||||
onSearchableFromInit={onSearchableFromInit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1824,6 +1737,9 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
// 注意,这里用关了哪些 store 里面的东西,TableContent 里面得也用一下
|
||||
// 因为 renderHeadCell 是 TableContent 回调的,tableContent 不重新渲染,这里面也不会重新渲染
|
||||
|
||||
const style = {...props.style};
|
||||
const [stickyStyle, stickyClassName] = store.getStickyStyles(
|
||||
column,
|
||||
@ -1908,7 +1824,30 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
);
|
||||
}
|
||||
|
||||
let affix = [];
|
||||
const prefix: Array<JSX.Element> = [];
|
||||
const affix: Array<JSX.Element> = [];
|
||||
|
||||
if (column.isPrimary && store.isNested) {
|
||||
(store.footable &&
|
||||
(store.footable.expandAll === false || store.footable.accordion)) ||
|
||||
(store.expandConfig &&
|
||||
(store.expandConfig.expandAll === false ||
|
||||
store.expandConfig.accordion)) ||
|
||||
prefix.push(
|
||||
<a
|
||||
key="expandBtn"
|
||||
className={cx(
|
||||
'Table-expandBtn2',
|
||||
store.allExpanded ? 'is-active' : ''
|
||||
)}
|
||||
// data-tooltip="展开/收起全部"
|
||||
// data-position="top"
|
||||
onClick={store.toggleExpandAll}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (column.searchable && column.name && !autoGenerateFilter) {
|
||||
affix.push(
|
||||
@ -2028,9 +1967,11 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
'Table-operationCell': column.type === 'operation'
|
||||
})}
|
||||
>
|
||||
{prefix}
|
||||
<div
|
||||
key="content"
|
||||
className={cx(
|
||||
`${ns}TableCell--title`,
|
||||
`TableCell--title`,
|
||||
column.pristine.className,
|
||||
column.pristine.labelClassName
|
||||
)}
|
||||
@ -2070,141 +2011,31 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classnames: cx,
|
||||
checkOnItemClick,
|
||||
popOverContainer,
|
||||
canAccessSuperData,
|
||||
itemBadge
|
||||
} = this.props;
|
||||
|
||||
if (column.name && item.rowSpans[column.name] === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const style: any = {...column.pristine.style};
|
||||
const [stickyStyle, stickyClassName] = store.getStickyStyles(
|
||||
column,
|
||||
store.filteredColumns
|
||||
);
|
||||
Object.assign(style, stickyStyle);
|
||||
|
||||
if (column.type === '__checkme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
<Checkbox
|
||||
classPrefix={ns}
|
||||
type={multiple ? 'checkbox' : 'radio'}
|
||||
checked={item.checked}
|
||||
disabled={item.checkdisable || !item.checkable}
|
||||
onChange={this.handleCheck.bind(this, item)}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
} else if (column.type === '__dragme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName, {
|
||||
'is-dragDisabled': !item.draggable
|
||||
})}
|
||||
>
|
||||
{item.draggable ? <Icon icon="drag" className="icon" /> : null}
|
||||
</td>
|
||||
);
|
||||
} else if (column.type === '__expandme') {
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
{item.depth > 2
|
||||
? Array.from({length: item.depth - 2}).map((_, index) => (
|
||||
<i key={index} className={cx('Table-divider-' + (index + 1))} />
|
||||
))
|
||||
: null}
|
||||
|
||||
{item.depth > 1 ? <i className={cx('Table-divider2')} /> : null}
|
||||
{item.depth > 1 ? <i className={cx('Table-divider3')} /> : null}
|
||||
|
||||
{item.expandable ? (
|
||||
<a
|
||||
className={cx(
|
||||
'Table-expandBtn',
|
||||
item.expanded ? 'is-active' : ''
|
||||
)}
|
||||
// data-tooltip="展开/收起"
|
||||
// data-position="top"
|
||||
onClick={item.toggleExpanded}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
let prefix: React.ReactNode = null;
|
||||
|
||||
if (
|
||||
!ignoreDrag &&
|
||||
column.isPrimary &&
|
||||
store.isNested &&
|
||||
store.draggable &&
|
||||
item.draggable
|
||||
) {
|
||||
prefix = (
|
||||
<a
|
||||
draggable
|
||||
onDragStart={this.handleDragStart}
|
||||
className={cx('Table-dragBtn')}
|
||||
>
|
||||
<Icon icon="drag" className="icon" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const canAccessSuperData =
|
||||
column.pristine.canAccessSuperData ?? this.props.canAccessSuperData;
|
||||
const subProps: any = {
|
||||
...props,
|
||||
// 操作列不下发loading,否则会导致操作栏里面的所有按钮都出现loading
|
||||
loading: column.type === 'operation' ? false : props.loading,
|
||||
btnDisabled: store.dragging,
|
||||
data: item.locals,
|
||||
value: column.name
|
||||
? resolveVariable(
|
||||
column.name,
|
||||
canAccessSuperData ? item.locals : item.data
|
||||
)
|
||||
: column.value,
|
||||
popOverContainer: this.getPopOverContainer,
|
||||
rowSpan: item.rowSpans[column.name as string],
|
||||
quickEditFormRef: this.subFormRef,
|
||||
cellPrefix: prefix,
|
||||
onImageEnlarge: this.handleImageEnlarge,
|
||||
canAccessSuperData,
|
||||
row: item,
|
||||
itemBadge,
|
||||
showBadge:
|
||||
!props.isHead &&
|
||||
itemBadge &&
|
||||
store.firstToggledColumnIndex === props.colIndex,
|
||||
onQuery: undefined,
|
||||
style,
|
||||
className: cx(column.pristine.className, stickyClassName)
|
||||
};
|
||||
delete subProps.label;
|
||||
|
||||
return render(
|
||||
region,
|
||||
{
|
||||
...column.pristine,
|
||||
column: column.pristine,
|
||||
type: 'cell'
|
||||
},
|
||||
subProps
|
||||
return (
|
||||
<Cell
|
||||
key={props.key}
|
||||
region={region}
|
||||
column={column}
|
||||
item={item}
|
||||
props={props}
|
||||
ignoreDrag={ignoreDrag}
|
||||
render={render}
|
||||
store={store}
|
||||
multiple={multiple}
|
||||
canAccessSuperData={canAccessSuperData}
|
||||
classnames={cx}
|
||||
classPrefix={ns}
|
||||
itemBadge={itemBadge}
|
||||
onCheck={this.handleCheck}
|
||||
onDragStart={this.handleDragStart}
|
||||
popOverContainer={this.getPopOverContainer}
|
||||
quickEditFormRef={this.subFormRef}
|
||||
onImageEnlarge={this.handleImageEnlarge}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2235,9 +2066,30 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
<div className={cx('Table-wrapper')}>
|
||||
<table
|
||||
ref={this.affixedTableRef}
|
||||
className={cx(tableClassName, 'is-layout-fixed')}
|
||||
className={cx(
|
||||
tableClassName,
|
||||
store.tableLayout === 'fixed' ? 'is-layout-fixed' : ''
|
||||
)}
|
||||
>
|
||||
<ColGroup columns={store.filteredColumns} store={store} />
|
||||
<colgroup>
|
||||
{store.filteredColumns.map(column => {
|
||||
const style: any = {
|
||||
width: `var(--Table-column-${column.index}-width)`
|
||||
};
|
||||
|
||||
if (store.tableLayout === 'auto') {
|
||||
style.minWidth = style.width;
|
||||
}
|
||||
|
||||
return (
|
||||
<col
|
||||
data-index={column.index}
|
||||
style={style}
|
||||
key={column.id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</colgroup>
|
||||
<thead>
|
||||
{columnsGroup.length ? (
|
||||
<tr>
|
||||
@ -2713,7 +2565,6 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
itemActions,
|
||||
dispatchEvent,
|
||||
onEvent,
|
||||
loading = false,
|
||||
loadingConfig
|
||||
} = this.props;
|
||||
|
||||
@ -2723,7 +2574,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
|
||||
return (
|
||||
<>
|
||||
{TableContent.renderItemActions({
|
||||
{renderItemActions({
|
||||
store,
|
||||
classnames: cx,
|
||||
render,
|
||||
@ -2746,7 +2597,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classnames={cx}
|
||||
columns={store.filteredColumns}
|
||||
columnsGroup={store.columnGroup}
|
||||
rows={store.rows}
|
||||
rows={store.items} // store.rows 是没有变更的,所以不会触发更新
|
||||
placeholder={placeholder}
|
||||
render={render}
|
||||
onMouseMove={
|
||||
@ -2781,10 +2632,10 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
translate={translate}
|
||||
dispatchEvent={dispatchEvent}
|
||||
onEvent={onEvent}
|
||||
loading={loading}
|
||||
loading={store.loading} // store 的同步较慢,所以统一用 store 来下发,否则会出现 props 和 store 变化触发子节点两次 re-rerender
|
||||
/>
|
||||
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={loading} />
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={store.loading} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -2851,7 +2702,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
'Table--unsaved': !!store.modified || !!store.moved,
|
||||
'Table--autoFillHeight': autoFillHeight
|
||||
})}
|
||||
style={style}
|
||||
style={store.buildStyles(style)}
|
||||
>
|
||||
{autoGenerateFilter ? this.renderAutoFilterForm() : null}
|
||||
{this.renderAffixHeader(tableClassName)}
|
||||
@ -2862,13 +2713,6 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
{this.renderTableContent()}
|
||||
|
||||
{
|
||||
// 利用这个将 table-layout: auto 转成 table-layout: fixed
|
||||
store.columnWidthReady ? null : (
|
||||
<span ref={this.updateTableInfoRef} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{footer}
|
||||
|
Loading…
Reference in New Issue
Block a user