mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
merge master
This commit is contained in:
commit
08f8089bd6
@ -1444,6 +1444,75 @@ run action ajax
|
||||
| copyFormat | `string` | `text/html` | 复制格式 |
|
||||
| content | [模板](../../docs/concepts/template) | - | 指定复制的内容。可用 `${xxx}` 取值 |
|
||||
|
||||
### 打印
|
||||
|
||||
> 6.2.0 及以后版本
|
||||
|
||||
打印页面中的某个组件,对应的组件需要配置 `testid`,如果要打印多个,可以使用 `"testids": ["x", "y"]` 来打印多个组件
|
||||
|
||||
```schema
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打印',
|
||||
level: 'primary',
|
||||
className: 'mr-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'print',
|
||||
args: {
|
||||
testid: 'mycrud'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "crud",
|
||||
"api": "/api/mock2/sample",
|
||||
"testid": "mycrud",
|
||||
"syncLocation": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "ID"
|
||||
},
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Rendering engine"
|
||||
},
|
||||
{
|
||||
"name": "browser",
|
||||
"label": "Browser"
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"label": "Platform(s)"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Engine version"
|
||||
},
|
||||
{
|
||||
"name": "grade",
|
||||
"label": "CSS grade"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ------- | ---------- | ------ | ----------------- |
|
||||
| testid | `string` | | 组件的 testid |
|
||||
| testids | `string[]` | - | 多个组件的 testid |
|
||||
|
||||
### 发送邮件
|
||||
|
||||
通过配置`actionType: 'email'`和邮件属性实现发送邮件操作。
|
||||
|
@ -303,7 +303,16 @@ export default {
|
||||
body: {
|
||||
type: 'form',
|
||||
name: 'sample-edit-form',
|
||||
api: '/api/sample/$id',
|
||||
data:{
|
||||
env: 'test'
|
||||
},
|
||||
api: {
|
||||
method:'post',
|
||||
url:'/api/sample/$id',
|
||||
messages:{
|
||||
success: '成功了-${env}'
|
||||
}
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
|
@ -8,7 +8,7 @@ export default {
|
||||
body: [
|
||||
{
|
||||
type: 'input-file',
|
||||
name: 'file.test',
|
||||
name: 'file',
|
||||
label: '选择 PDF 文件预览效果(不会上传到服务器)',
|
||||
asBlob: true,
|
||||
accept: '.pdf'
|
||||
@ -16,7 +16,7 @@ export default {
|
||||
{
|
||||
type: 'pdf-viewer',
|
||||
id: 'pdf-viewer',
|
||||
name: 'file.test'
|
||||
name: 'file'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
51
packages/amis-core/src/actions/PrintAction.ts
Normal file
51
packages/amis-core/src/actions/PrintAction.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import {printElements} from '../utils/printElement';
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {
|
||||
RendererAction,
|
||||
ListenerAction,
|
||||
ListenerContext,
|
||||
registerAction
|
||||
} from './Action';
|
||||
|
||||
export interface IPrintAction extends ListenerAction {
|
||||
actionType: 'copy';
|
||||
args: {
|
||||
testid?: string;
|
||||
testids?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印动作
|
||||
*
|
||||
* @export
|
||||
* @class PrintAction
|
||||
* @implements {Action}
|
||||
*/
|
||||
export class PrintAction implements RendererAction {
|
||||
async run(
|
||||
action: IPrintAction,
|
||||
renderer: ListenerContext,
|
||||
event: RendererEvent<any>
|
||||
) {
|
||||
if (action.args?.testid) {
|
||||
const element = document.querySelector(
|
||||
`[data-testid='${action.args.testid}']`
|
||||
);
|
||||
if (element) {
|
||||
printElements([element]);
|
||||
}
|
||||
} else if (action.args?.testids) {
|
||||
const elements: Element[] = [];
|
||||
action.args.testids.forEach(testid => {
|
||||
const element = document.querySelector(`[data-testid='${testid}']`);
|
||||
if (element) {
|
||||
elements.push(element);
|
||||
}
|
||||
});
|
||||
printElements(elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerAction('print', new PrintAction());
|
@ -19,5 +19,6 @@ import './EmailAction';
|
||||
import './LinkAction';
|
||||
import './ToastAction';
|
||||
import './PageAction';
|
||||
import './PrintAction';
|
||||
|
||||
export * from './Action';
|
||||
|
@ -206,7 +206,8 @@ export {
|
||||
splitTarget,
|
||||
CustomStyle,
|
||||
enableDebug,
|
||||
disableDebug
|
||||
disableDebug,
|
||||
envOverwrite
|
||||
};
|
||||
|
||||
export function render(
|
||||
|
@ -49,7 +49,7 @@ import LazyComponent from '../components/LazyComponent';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
|
||||
import type {LabelAlign} from './Item';
|
||||
import {injectObjectChain} from '../utils';
|
||||
import {buildTestId, injectObjectChain} from '../utils';
|
||||
import {reaction} from 'mobx';
|
||||
|
||||
export interface FormHorizontal {
|
||||
@ -1808,7 +1808,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
render,
|
||||
staticClassName,
|
||||
static: isStatic = false,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
testid
|
||||
} = this.props;
|
||||
|
||||
const {restError} = store;
|
||||
@ -1840,6 +1841,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
)}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
noValidate
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{/* 实现回车自动提交 */}
|
||||
<input type="submit" style={{display: 'none'}} />
|
||||
|
@ -6,7 +6,8 @@ import {ServerError} from '../utils/errors';
|
||||
import {normalizeApiResponseData} from '../utils/api';
|
||||
import {replaceText} from '../utils/replaceText';
|
||||
import {concatData} from '../utils/concatData';
|
||||
|
||||
import {envOverwrite} from '../envOverwrite';
|
||||
import {filter} from 'amis-core';
|
||||
export const ServiceStore = iRendererStore
|
||||
.named('ServiceStore')
|
||||
.props({
|
||||
@ -54,7 +55,7 @@ export const ServiceStore = iRendererStore
|
||||
}
|
||||
|
||||
function updateMessage(msg?: string, error: boolean = false) {
|
||||
self.msg = (msg && String(msg)) || '';
|
||||
self.msg = (msg && filter(msg, self.data)) || '';
|
||||
self.error = error;
|
||||
}
|
||||
|
||||
@ -445,6 +446,7 @@ export const ServiceStore = iRendererStore
|
||||
} else {
|
||||
if (json.data) {
|
||||
const env = getEnv(self);
|
||||
json.data = envOverwrite(json.data, env.locale);
|
||||
json.data = replaceText(
|
||||
json.data,
|
||||
env.replaceText,
|
||||
|
@ -1288,7 +1288,7 @@ export const TableStore = iRendererStore
|
||||
typeof column.pristine.width === 'number'
|
||||
? `width: ${column.pristine.width}px;`
|
||||
: column.pristine.width
|
||||
? `width: ${column.pristine.width};`
|
||||
? `width: ${column.pristine.width};min-width: ${column.pristine.width};`
|
||||
: '' // todo 可能需要让修改过列宽的保持相应宽度,目前这样相当于重置了
|
||||
}`;
|
||||
});
|
||||
|
@ -2,3 +2,10 @@ export const chromeVersion = (function getChromeVersion() {
|
||||
const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
|
||||
return raw ? parseInt(raw[2], 10) : false;
|
||||
})();
|
||||
|
||||
export const isSafari =
|
||||
navigator.vendor &&
|
||||
navigator.vendor.indexOf('Apple') > -1 &&
|
||||
navigator.userAgent &&
|
||||
navigator.userAgent.indexOf('CriOS') == -1 &&
|
||||
navigator.userAgent.indexOf('FxiOS') == -1;
|
||||
|
76
packages/amis-core/src/utils/printElement.ts
Normal file
76
packages/amis-core/src/utils/printElement.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 打印元素,参考 https://github.com/szepeshazi/print-elements 里的实现
|
||||
* 原理就是遍历节点加上打印样式,然后打印完了再清理掉
|
||||
* 对代码做了改造和优化
|
||||
*/
|
||||
|
||||
const hideFromPrintClass = 'pe-no-print';
|
||||
const preservePrintClass = 'pe-preserve-print';
|
||||
const preserveAncestorClass = 'pe-preserve-ancestor';
|
||||
const bodyElementName = 'BODY';
|
||||
|
||||
function hide(element: Element) {
|
||||
if (!element.classList.contains(preservePrintClass)) {
|
||||
element.classList.add(hideFromPrintClass);
|
||||
}
|
||||
}
|
||||
|
||||
function preserve(element: Element, isStartingElement: boolean) {
|
||||
element.classList.remove(hideFromPrintClass);
|
||||
element.classList.add(preservePrintClass);
|
||||
if (!isStartingElement) {
|
||||
element.classList.add(preserveAncestorClass);
|
||||
}
|
||||
}
|
||||
|
||||
function clean(element: Element) {
|
||||
element.classList.remove(hideFromPrintClass);
|
||||
element.classList.remove(preservePrintClass);
|
||||
element.classList.remove(preserveAncestorClass);
|
||||
}
|
||||
|
||||
function walkSiblings(element: Element, callback: (element: Element) => void) {
|
||||
let sibling = element.previousElementSibling;
|
||||
while (sibling) {
|
||||
callback(sibling);
|
||||
sibling = sibling.previousElementSibling;
|
||||
}
|
||||
sibling = element.nextElementSibling;
|
||||
while (sibling) {
|
||||
callback(sibling);
|
||||
sibling = sibling.nextElementSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function attachPrintClasses(element: Element, isStartingElement: boolean) {
|
||||
preserve(element, isStartingElement);
|
||||
walkSiblings(element, hide);
|
||||
}
|
||||
|
||||
function cleanup(element: Element, isStartingElement: boolean) {
|
||||
clean(element);
|
||||
walkSiblings(element, clean);
|
||||
}
|
||||
|
||||
function walkTree(
|
||||
element: Element,
|
||||
callback: (element: Element, isStartingElement: boolean) => void
|
||||
) {
|
||||
let currentElement: Element | null = element;
|
||||
callback(currentElement, true);
|
||||
currentElement = currentElement.parentElement;
|
||||
while (currentElement && currentElement.nodeName !== bodyElementName) {
|
||||
callback(currentElement, false);
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
export function printElements(elements: Element[]) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
walkTree(elements[i], attachPrintClasses);
|
||||
}
|
||||
window.print();
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
walkTree(elements[i], cleanup);
|
||||
}
|
||||
}
|
@ -324,7 +324,7 @@ export default class TransferTableOption extends React.Component<
|
||||
return {
|
||||
type: 'action',
|
||||
actionType: 'dialog',
|
||||
label: '添加表格列',
|
||||
label: '设置表格列',
|
||||
level: 'enhance',
|
||||
dialog: {
|
||||
title: '设置表格列选项',
|
||||
@ -348,12 +348,14 @@ export default class TransferTableOption extends React.Component<
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'label',
|
||||
placeholder: '标题'
|
||||
placeholder: '标题',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
placeholder: '绑定字段名'
|
||||
placeholder: '绑定字段名',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
@ -417,7 +419,7 @@ export default class TransferTableOption extends React.Component<
|
||||
{
|
||||
type: 'action',
|
||||
actionType: 'dialog',
|
||||
label: '添加表格行',
|
||||
label: '设置表格行',
|
||||
level: 'enhance',
|
||||
disabled: columns && columns.length === 0,
|
||||
block: true,
|
||||
|
@ -2,11 +2,16 @@
|
||||
height: px2rem(30px);
|
||||
line-height: px2rem(30px);
|
||||
font-size: px2rem(12px);
|
||||
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0 var(--gap-md);
|
||||
border-bottom: var(--borderWidth) solid var(--borderColor);
|
||||
|
||||
a {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
13
packages/amis-ui/scss/components/_print.scss
Normal file
13
packages/amis-ui/scss/components/_print.scss
Normal file
@ -0,0 +1,13 @@
|
||||
@media print {
|
||||
.pe-no-print {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.pe-preserve-ancestor {
|
||||
display: block !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
@ -143,3 +143,5 @@
|
||||
@import '../components/menu';
|
||||
@import '../components/overflow-tpl';
|
||||
@import '../components/pdf_viewer';
|
||||
|
||||
@import '../components/print';
|
||||
|
104
packages/amis/__tests__/renderers/App.test.tsx
Normal file
104
packages/amis/__tests__/renderers/App.test.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import React from 'react';
|
||||
import Action from '../../src/renderers/Action';
|
||||
import * as renderer from 'react-test-renderer';
|
||||
import {
|
||||
render,
|
||||
fireEvent,
|
||||
cleanup,
|
||||
screen,
|
||||
waitFor,
|
||||
within
|
||||
} from '@testing-library/react';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
import '../../src';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
// 关联 issue https://github.com/baidu/amis/issues/9564
|
||||
test('Renderers:App locale', async () => {
|
||||
const fetcher = jest.fn().mockImplementation((api, options) => {
|
||||
if (api.url.startsWith('/pageList')) {
|
||||
return Promise.resolve({
|
||||
status: 200,
|
||||
data: {
|
||||
status: 0,
|
||||
msg: '',
|
||||
data: {
|
||||
pages: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
'label': 'Home',
|
||||
'icon': 'fa fa-home',
|
||||
'url': '/admin/page/home',
|
||||
'schemaApi': '/pageDetail',
|
||||
'isDefaultPage': true,
|
||||
'sort': 100,
|
||||
'zh-CN': {
|
||||
label: '首页'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (api.url.startsWith('/pageDetail')) {
|
||||
return Promise.resolve({
|
||||
status: 200,
|
||||
data: {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
'type': 'input-text',
|
||||
'name': 'a',
|
||||
'label': 'dev',
|
||||
'content': 'False',
|
||||
'zh-CN': {
|
||||
label: '开发环境'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
status: 200,
|
||||
data: {
|
||||
status: 404,
|
||||
msg: 'notFound'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'app',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: '/pageList'
|
||||
}
|
||||
},
|
||||
{
|
||||
locale: 'zh-CN'
|
||||
},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await wait(500);
|
||||
|
||||
const link = container.querySelector('nav li span');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link!.textContent).toBe('首页');
|
||||
|
||||
const inputLabel = container.querySelector('.cxd-Form-label');
|
||||
expect(inputLabel).toBeInTheDocument();
|
||||
expect(inputLabel!.textContent).toBe('开发环境');
|
||||
});
|
@ -448,7 +448,7 @@ exports[`Renderer:input table add 1`] = `
|
||||
<th
|
||||
class="is-sticky is-sticky-right is-sticky-first-right cxd-Table-operationCell"
|
||||
data-index="5"
|
||||
style="right: 0px;"
|
||||
style="width: 150px; min-width: 150px;"
|
||||
>
|
||||
<div
|
||||
class="cxd-TableCell--title v-middle nowrap"
|
||||
|
@ -809,3 +809,141 @@ test('Renderer:input-table formula', async () => {
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
// 对应 github issue: https://github.com/baidu/amis/issues/9494
|
||||
test('Renderer:input-table autoFill', async () => {
|
||||
const onSubmit = jest.fn();
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
title: 'Hello low code',
|
||||
body: [
|
||||
{
|
||||
type: 'form',
|
||||
api: '/api/mock2/form/saveForm',
|
||||
body: [
|
||||
{
|
||||
type: 'input-table',
|
||||
name: 'table',
|
||||
label: '表格表单',
|
||||
columns: [
|
||||
{
|
||||
label: '名称',
|
||||
name: 'name',
|
||||
quickEdit: {
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
id: 'u:514910e73695'
|
||||
},
|
||||
id: 'u:97d119520d7c'
|
||||
},
|
||||
{
|
||||
label: '分数',
|
||||
name: 'score',
|
||||
quickEdit: {
|
||||
type: 'input-number',
|
||||
name: 'score',
|
||||
id: 'u:644f5984ff07'
|
||||
},
|
||||
id: 'u:60636ff9ed10'
|
||||
},
|
||||
{
|
||||
label: '等级',
|
||||
name: 'level',
|
||||
quickEdit: {
|
||||
type: 'select',
|
||||
name: 'level',
|
||||
autoFill: {
|
||||
id: '$id'
|
||||
},
|
||||
id: 'u:38014752298b',
|
||||
options: [
|
||||
{
|
||||
label: 'a',
|
||||
value: '111',
|
||||
id: 111
|
||||
},
|
||||
{
|
||||
label: 'a1',
|
||||
value: '1121',
|
||||
id: 222
|
||||
}
|
||||
]
|
||||
},
|
||||
id: 'u:bc682229ad4f'
|
||||
}
|
||||
],
|
||||
addable: true,
|
||||
footerAddBtn: {
|
||||
label: '新增',
|
||||
icon: 'fa fa-plus',
|
||||
id: 'u:a0d2d9eab4f7'
|
||||
},
|
||||
strictMode: true,
|
||||
id: 'u:c296ba75753c',
|
||||
minLength: 0,
|
||||
editable: true,
|
||||
removable: true
|
||||
}
|
||||
],
|
||||
id: 'u:a2f24ee3ab2d',
|
||||
debug: true
|
||||
}
|
||||
],
|
||||
id: 'u:09eedced8bb6',
|
||||
asideResizor: false,
|
||||
style: {
|
||||
boxShadow: ' 0px 0px 0px 0px transparent'
|
||||
},
|
||||
pullRefresh: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
{
|
||||
onSubmit: onSubmit
|
||||
},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
|
||||
const add = container.querySelector('.cxd-InputTable-toolbar button');
|
||||
fireEvent.click(add!);
|
||||
await wait(200);
|
||||
|
||||
fireEvent.change(container.querySelector('input[name="name"]')!, {
|
||||
target: {value: 'a1'}
|
||||
});
|
||||
await wait(200);
|
||||
|
||||
fireEvent.change(container.querySelector('input[name="score"]')!, {
|
||||
target: {value: '123'}
|
||||
});
|
||||
await wait(200);
|
||||
|
||||
fireEvent.click(container.querySelector('.cxd-Select')!);
|
||||
await wait(200);
|
||||
fireEvent.click(container.querySelector('.cxd-Select-menu [role="option"]')!);
|
||||
await wait(200);
|
||||
|
||||
fireEvent.click(container.querySelector('.cxd-OperationField button')!);
|
||||
await wait(200);
|
||||
|
||||
const submitBtn = container.querySelector('button[type=submit]');
|
||||
fireEvent.click(submitBtn!);
|
||||
await wait(200);
|
||||
|
||||
expect(onSubmit).toBeCalled();
|
||||
expect(onSubmit.mock.calls[0][0]).toEqual({
|
||||
table: [
|
||||
{
|
||||
id: 111,
|
||||
name: 'a1',
|
||||
score: 123,
|
||||
level: '111'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -1,137 +1,156 @@
|
||||
import {render} from '@testing-library/react';
|
||||
import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv} from '../helper';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
|
||||
const tag = (label: string) => render(
|
||||
amisRender(
|
||||
{type: 'tag', label},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
const tag = (label: string) =>
|
||||
render(amisRender({type: 'tag', label}, {}, makeEnv({}))).container;
|
||||
|
||||
test('Renderer:mapping width object map', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: "漂亮",
|
||||
2: "开心",
|
||||
3: "惊吓",
|
||||
4: "紧张",
|
||||
'*': '其他',
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: '漂亮',
|
||||
2: '开心',
|
||||
3: '惊吓',
|
||||
4: '紧张',
|
||||
'*': '其他'
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value1 = setup(1).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value1.innerHTML).toBe('漂亮');
|
||||
|
||||
const value5 = setup(5).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value5 = setup(5).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value5.innerHTML).toBe('其他');
|
||||
});
|
||||
|
||||
test('Renderer:mapping width array map', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{1: "漂亮"},
|
||||
{2: "开心"},
|
||||
{3: "惊吓"},
|
||||
{4: "紧张"},
|
||||
{'*': '其他'}
|
||||
],
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{1: '漂亮'},
|
||||
{2: '开心'},
|
||||
{3: '惊吓'},
|
||||
{4: '紧张'},
|
||||
{'*': '其他'}
|
||||
],
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value1 = setup(1).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value1.innerHTML).toBe('漂亮');
|
||||
|
||||
const value5 = setup(5).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value5 = setup(5).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value5.innerHTML).toBe('其他');
|
||||
});
|
||||
|
||||
test('Renderer:mapping attr: valueField and labelField', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{
|
||||
name: 1,
|
||||
text: '漂亮'
|
||||
},
|
||||
{
|
||||
name: 2,
|
||||
text: '开心'
|
||||
},
|
||||
{
|
||||
name: '*',
|
||||
text: '其他'
|
||||
}
|
||||
],
|
||||
labelField: 'text',
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{
|
||||
name: 1,
|
||||
text: '漂亮'
|
||||
},
|
||||
{
|
||||
name: 2,
|
||||
text: '开心'
|
||||
},
|
||||
{
|
||||
name: '*',
|
||||
text: '其他'
|
||||
}
|
||||
],
|
||||
labelField: 'text',
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value1 = setup(1).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value1.innerHTML).toBe('漂亮');
|
||||
|
||||
const value5 = setup(5).querySelector('.cxd-MappingField .cxd-TplField span')! as HTMLElement;
|
||||
const value5 = setup(5).querySelector(
|
||||
'.cxd-MappingField .cxd-TplField span'
|
||||
)! as HTMLElement;
|
||||
expect(value5.innerHTML).toBe('其他');
|
||||
});
|
||||
|
||||
test('Renderer:mapping attr: itemSchema when simple map', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{1: "漂亮"},
|
||||
{2: "开心"},
|
||||
{3: "惊吓"},
|
||||
{4: "紧张"},
|
||||
{'*': '其他'}
|
||||
],
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {}),
|
||||
itemSchema: {
|
||||
type: 'tag',
|
||||
label: '${item}'
|
||||
}
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{1: '漂亮'},
|
||||
{2: '开心'},
|
||||
{3: '惊吓'},
|
||||
{4: '紧张'},
|
||||
{'*': '其他'}
|
||||
],
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {}),
|
||||
itemSchema: {
|
||||
type: 'tag',
|
||||
label: '${item}'
|
||||
}
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField')! as HTMLElement;
|
||||
@ -142,37 +161,40 @@ test('Renderer:mapping attr: itemSchema when simple map', async () => {
|
||||
});
|
||||
|
||||
test('Renderer:mapping attr: itemSchema when normal map', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{
|
||||
name: 1,
|
||||
text: '漂亮'
|
||||
},
|
||||
{
|
||||
name: 2,
|
||||
text: '开心'
|
||||
},
|
||||
{
|
||||
name: '*',
|
||||
text: '其他'
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: [
|
||||
{
|
||||
name: 1,
|
||||
text: '漂亮'
|
||||
},
|
||||
{
|
||||
name: 2,
|
||||
text: '开心'
|
||||
},
|
||||
{
|
||||
name: '*',
|
||||
text: '其他'
|
||||
}
|
||||
],
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {}),
|
||||
itemSchema: {
|
||||
type: 'tag',
|
||||
label: '${name} ${text}'
|
||||
}
|
||||
],
|
||||
valueField: 'name',
|
||||
...(value !== undefined ? {value} : {}),
|
||||
itemSchema: {
|
||||
type: 'tag',
|
||||
label: '${name} ${text}'
|
||||
}
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField')! as HTMLElement;
|
||||
@ -204,23 +226,24 @@ test('Renderer:mapping', async () => {
|
||||
});
|
||||
|
||||
test('Renderer:mapping html', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: "<span class='label label-info'>漂亮</span>",
|
||||
2: "<span class='label label-success'>开心</span>",
|
||||
3: "<span class='label label-danger'>惊吓</span>",
|
||||
4: "<span class='label label-warning'>紧张</span>",
|
||||
'*': '其他'
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: "<span class='label label-info'>漂亮</span>",
|
||||
2: "<span class='label label-success'>开心</span>",
|
||||
3: "<span class='label label-danger'>惊吓</span>",
|
||||
4: "<span class='label label-warning'>紧张</span>",
|
||||
'*': '其他'
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
expect(setup()).toMatchSnapshot();
|
||||
expect(setup(1)).toMatchSnapshot();
|
||||
@ -228,25 +251,28 @@ test('Renderer:mapping html', async () => {
|
||||
});
|
||||
|
||||
test('Renderer:mapping schema', async () => {
|
||||
const setup = (value?: any) => render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: {type: 'tag', label: '漂亮'},
|
||||
2: {type: 'tag', label: '开心'},
|
||||
3: {type: 'tag', label: '惊吓'},
|
||||
4: {type: 'tag', label: '紧张'},
|
||||
'*': {type: 'tag', label: '其他'}
|
||||
const setup = (value?: any) =>
|
||||
render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'mapping',
|
||||
map: {
|
||||
1: {type: 'tag', label: '漂亮'},
|
||||
2: {type: 'tag', label: '开心'},
|
||||
3: {type: 'tag', label: '惊吓'},
|
||||
4: {type: 'tag', label: '紧张'},
|
||||
'*': {type: 'tag', label: '其他'}
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
...(value !== undefined ? {value} : {})
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
).container;
|
||||
|
||||
const noValue = setup().querySelector('.cxd-MappingField .text-muted')! as HTMLElement;
|
||||
const noValue = setup().querySelector(
|
||||
'.cxd-MappingField .text-muted'
|
||||
)! as HTMLElement;
|
||||
expect(noValue.innerHTML).toBe('-');
|
||||
|
||||
const value1 = setup(1).querySelector('.cxd-MappingField')! as HTMLElement;
|
||||
@ -255,3 +281,68 @@ test('Renderer:mapping schema', async () => {
|
||||
const value5 = setup(5).querySelector('.cxd-MappingField')! as HTMLElement;
|
||||
expect(value5.innerHTML).toBe(tag('其他').innerHTML);
|
||||
});
|
||||
|
||||
// 对应 issue https://github.com/baidu/amis/issues/9613
|
||||
test('Renderer:mapping schema status', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
status: 1,
|
||||
id: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'table',
|
||||
title: '表格',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: '状态2',
|
||||
type: 'mapping',
|
||||
map: {
|
||||
'*': {
|
||||
type: 'status',
|
||||
map: {
|
||||
'0': 'schedule',
|
||||
'1': 'rolling',
|
||||
'2': 'success',
|
||||
'3': 'fail',
|
||||
'4': 'warning'
|
||||
},
|
||||
labelMap: {
|
||||
'2': '任务成功',
|
||||
'1': '处理中',
|
||||
'3': '异常终止',
|
||||
'0': '等待中',
|
||||
'4': '已过期'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
|
||||
expect(
|
||||
[].slice
|
||||
.call(container.querySelectorAll('tbody td'))
|
||||
.map((td: any) => td.textContent)
|
||||
).toEqual(['1', '处理中']);
|
||||
});
|
||||
|
@ -8,7 +8,13 @@ import {
|
||||
SpinnerExtraProps
|
||||
} from 'amis-ui';
|
||||
import {Layout} from 'amis-ui';
|
||||
import {Renderer, RendererProps, filter, replaceText} from 'amis-core';
|
||||
import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
envOverwrite,
|
||||
filter,
|
||||
replaceText
|
||||
} from 'amis-core';
|
||||
import {
|
||||
BaseSchema,
|
||||
SchemaApi,
|
||||
@ -244,11 +250,13 @@ export default class App extends React.Component<AppProps, object> {
|
||||
store,
|
||||
env,
|
||||
showFullBreadcrumbPath = false,
|
||||
showBreadcrumbHomePath = true
|
||||
showBreadcrumbHomePath = true,
|
||||
locale
|
||||
} = this.props;
|
||||
|
||||
if (isEffectiveApi(api, store.data)) {
|
||||
const json = await store.fetchInitData(api, store.data, {});
|
||||
|
||||
if (env.replaceText) {
|
||||
json.data = replaceText(
|
||||
json.data,
|
||||
@ -258,6 +266,8 @@ export default class App extends React.Component<AppProps, object> {
|
||||
}
|
||||
|
||||
if (json?.data.pages) {
|
||||
json.data = envOverwrite(json.data, locale);
|
||||
|
||||
store.setPages(json.data.pages);
|
||||
store.updateActivePage(
|
||||
Object.assign({}, env ?? {}, {
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
RendererProps,
|
||||
evalExpressionWithConditionBuilder,
|
||||
filterTarget,
|
||||
mapTree
|
||||
mapTree,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {SchemaNode, Schema, ActionObject, PlainObject} from 'amis-core';
|
||||
import {CRUDStore, ICRUDStore} from 'amis-core';
|
||||
@ -2516,6 +2517,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
onSearchableFromInit,
|
||||
headerToolbarRender,
|
||||
footerToolbarRender,
|
||||
testid,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -2526,6 +2528,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
'is-mobile': isMobile()
|
||||
})}
|
||||
style={style}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{filter && (!store.filterTogggable || store.filterVisible)
|
||||
? render(
|
||||
|
@ -30,7 +30,8 @@ import {
|
||||
isApiOutdated,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
parsePrimitiveQueryString
|
||||
parsePrimitiveQueryString,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {Html, SpinnerExtraProps} from 'amis-ui';
|
||||
import {
|
||||
@ -1308,6 +1309,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
||||
columnsTogglable,
|
||||
headerToolbarClassName,
|
||||
footerToolbarClassName,
|
||||
testid,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@ -1317,6 +1319,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
||||
'is-loading': store.loading
|
||||
})}
|
||||
style={style}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
<div className={cx('Crud2-filter')}>
|
||||
{this.renderFilter(filterSchema)}
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {DndContainer as DndWrapper} from 'amis-ui';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
@ -195,7 +196,8 @@ export default class Container<T> extends React.Component<
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
baseControlClassName,
|
||||
testid
|
||||
} = this.props;
|
||||
const finalDraggable: boolean = isPureVariable(draggable)
|
||||
? resolveVariableAndFilter(draggable, data, '| raw')
|
||||
@ -231,6 +233,7 @@ export default class Container<T> extends React.Component<
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
style={buildStyle(style, data)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderBody()}
|
||||
<CustomStyle
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {Schema} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection, SchemaObject} from '../Schema';
|
||||
@ -111,7 +112,8 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
classnames: cx
|
||||
classnames: cx,
|
||||
testid
|
||||
} = this.props;
|
||||
const styleVar = buildStyle(style, data);
|
||||
const flexStyle = {
|
||||
@ -150,6 +152,7 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
themeCss: wrapperCustomStyle
|
||||
})
|
||||
)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{(Array.isArray(items) ? items : items ? [items] : []).map(
|
||||
(item, key) =>
|
||||
|
@ -73,7 +73,7 @@ function normalizeValue(value: any, language?: string) {
|
||||
export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
static defaultProps: Partial<DiffEditorProps> = {
|
||||
language: 'javascript',
|
||||
theme: 'vs',
|
||||
editorTheme: 'vs',
|
||||
options: {
|
||||
automaticLayout: false,
|
||||
selectOnLineNumbers: true,
|
||||
@ -304,7 +304,7 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
size,
|
||||
options,
|
||||
language,
|
||||
theme,
|
||||
editorTheme,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
|
||||
@ -326,7 +326,7 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
language={language}
|
||||
theme={theme}
|
||||
editorTheme={editorTheme}
|
||||
editorDidMount={this.handleEditorMounted}
|
||||
editorFactory={this.editorFactory}
|
||||
options={{
|
||||
|
@ -5,7 +5,8 @@ import {
|
||||
RendererProps,
|
||||
buildStyle,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import pick from 'lodash/pick';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
@ -212,7 +213,8 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss
|
||||
themeCss,
|
||||
testid
|
||||
} = this.props;
|
||||
const styleVar = buildStyle(style, data);
|
||||
return (
|
||||
@ -239,6 +241,7 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||
})
|
||||
)}
|
||||
style={styleVar}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderColumns(this.props.columns)}
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={loading} />
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {buildTestId, Renderer, RendererProps} from 'amis-core';
|
||||
import {Api, SchemaNode, Schema, ActionObject} from 'amis-core';
|
||||
import {isVisible} from 'amis-core';
|
||||
import {BaseSchema, SchemaObject} from '../Schema';
|
||||
@ -172,7 +172,8 @@ export default class Grid2D extends React.Component<Grid2DProps, object> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {grids, cols, gap, gapRow, width, rowHeight, style} = this.props;
|
||||
const {grids, cols, gap, gapRow, width, rowHeight, style, testid} =
|
||||
this.props;
|
||||
|
||||
const templateColumns = new Array(cols);
|
||||
templateColumns.fill('1fr');
|
||||
@ -214,7 +215,11 @@ export default class Grid2D extends React.Component<Grid2DProps, object> {
|
||||
gridTemplateRows: templateRows.join(' ')
|
||||
};
|
||||
|
||||
return <div style={curStyle}>{this.renderGrids()}</div>;
|
||||
return (
|
||||
<div style={curStyle} {...buildTestId(testid)}>
|
||||
{this.renderGrids()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ export const MappingField = withStore(props =>
|
||||
}
|
||||
|
||||
renderViewValue(value: any) {
|
||||
const {render, itemSchema, data, labelField} = this.props;
|
||||
const {render, itemSchema, data, labelField, name} = this.props;
|
||||
|
||||
if (!itemSchema) {
|
||||
let label = value;
|
||||
@ -265,6 +265,12 @@ export const MappingField = withStore(props =>
|
||||
// object 也没有 type,不能作为schema渲染
|
||||
// 默认取 label 字段
|
||||
label = value['label'];
|
||||
} else {
|
||||
// 不会下发 value 了,所以要把 name 下发一下
|
||||
label = {
|
||||
name,
|
||||
...label
|
||||
};
|
||||
}
|
||||
} else {
|
||||
label = value[labelField || 'label'];
|
||||
|
@ -28,7 +28,7 @@ export interface PdfViewerSchema extends BaseSchema {
|
||||
*/
|
||||
src?: string;
|
||||
/**
|
||||
* 文件取值,一般配个表单使用
|
||||
* 文件取值,一般配合表单使用
|
||||
*/
|
||||
name?: string;
|
||||
width?: number;
|
||||
|
@ -140,6 +140,7 @@ export const HocQuickEdit =
|
||||
this.handleInit = this.handleInit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleFormItemChange = this.handleFormItemChange.bind(this);
|
||||
this.handleBulkChange = this.handleBulkChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
isOpened: false
|
||||
@ -379,6 +380,18 @@ export const HocQuickEdit =
|
||||
);
|
||||
}
|
||||
|
||||
// autoFill 是通过 onBulkChange 触发的
|
||||
// quickEdit 需要拦截这个,否则修改的数据就是错的
|
||||
handleBulkChange(values: any) {
|
||||
const {onQuickChange, quickEdit} = this.props;
|
||||
onQuickChange(
|
||||
values,
|
||||
(quickEdit as QuickEditConfig).saveImmediately,
|
||||
false,
|
||||
quickEdit as QuickEditConfig
|
||||
);
|
||||
}
|
||||
|
||||
openQuickEdit() {
|
||||
currentOpened = this;
|
||||
this.setState({
|
||||
@ -595,6 +608,7 @@ export const HocQuickEdit =
|
||||
mode: 'normal',
|
||||
value: getPropValue(this.props) ?? '',
|
||||
onChange: this.handleFormItemChange,
|
||||
onBulkChange: this.handleBulkChange,
|
||||
ref: this.formItemRef,
|
||||
defaultStatic: false
|
||||
});
|
||||
@ -608,6 +622,7 @@ export const HocQuickEdit =
|
||||
simpleMode: true,
|
||||
onInit: this.handleInit,
|
||||
onChange: this.handleChange,
|
||||
onBulkChange: this.handleBulkChange,
|
||||
formLazyChange: false,
|
||||
canAccessSuperData,
|
||||
disabled,
|
||||
|
@ -1,5 +1,10 @@
|
||||
import React from 'react';
|
||||
import {chromeVersion, type IColumn, type ITableStore} from 'amis-core';
|
||||
import {
|
||||
chromeVersion,
|
||||
isSafari,
|
||||
type IColumn,
|
||||
type ITableStore
|
||||
} from 'amis-core';
|
||||
import {observer} from 'mobx-react';
|
||||
|
||||
export function ColGroup({
|
||||
@ -37,7 +42,9 @@ export function ColGroup({
|
||||
// 低版本同时设置 thead>th
|
||||
// The problem is min-width CSS property.
|
||||
// Before Chrome 91, min-width was ignored on COL elements. 91 no longer ignores it.
|
||||
if (typeof chromeVersion === 'number' && chromeVersion < 91) {
|
||||
//
|
||||
// 同时 safari 也存在类似问题,设置 colgroup>col 的 width 属性无效
|
||||
if (isSafari || (typeof chromeVersion === 'number' && chromeVersion < 91)) {
|
||||
React.useEffect(() => {
|
||||
if (domRef.current) {
|
||||
const ths = [].slice.call(
|
||||
|
@ -41,7 +41,8 @@ import {
|
||||
resizeSensor,
|
||||
offset,
|
||||
getStyleNumber,
|
||||
getPropValue
|
||||
getPropValue,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {
|
||||
Button,
|
||||
@ -1301,8 +1302,8 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
if (this.resizeLine) {
|
||||
return;
|
||||
}
|
||||
this.props.store.syncTableWidth();
|
||||
this.props.store.initTableWidth();
|
||||
this.props.store.syncTableWidth();
|
||||
this.handleOutterScroll();
|
||||
callback && setTimeout(callback, 20);
|
||||
}
|
||||
@ -2803,7 +2804,8 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
affixHeader,
|
||||
autoFillHeight,
|
||||
autoGenerateFilter,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
testid
|
||||
} = this.props;
|
||||
|
||||
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
|
||||
@ -2822,6 +2824,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
'Table--autoFillHeight': autoFillHeight
|
||||
})}
|
||||
style={store.buildStyles(style)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{autoGenerateFilter ? this.renderAutoFilterForm() : null}
|
||||
{this.renderAffixHeader(tableClassName)}
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
RendererProps,
|
||||
resolveMappingObject,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
buildTestId
|
||||
} from 'amis-core';
|
||||
import {BaseSchema, SchemaObject} from '../Schema';
|
||||
|
||||
@ -276,6 +277,7 @@ export default class TableView extends React.Component<TableViewProps, object> {
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
testid,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
|
||||
@ -298,6 +300,7 @@ export default class TableView extends React.Component<TableViewProps, object> {
|
||||
})
|
||||
)}
|
||||
style={{width: width, borderCollapse: 'collapse'}}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderCaption()}
|
||||
{this.renderCols()}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {buildTestId, Renderer, RendererProps} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection} from '../Schema';
|
||||
import {resolveVariable} from 'amis-core';
|
||||
import {SchemaNode} from 'amis-core';
|
||||
@ -59,7 +59,15 @@ export default class Wrapper extends React.Component<WrapperProps, object> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {className, size, classnames: cx, style, data, wrap} = this.props;
|
||||
const {
|
||||
className,
|
||||
size,
|
||||
classnames: cx,
|
||||
style,
|
||||
data,
|
||||
wrap,
|
||||
testid
|
||||
} = this.props;
|
||||
|
||||
// 期望不要使用,给 form controls 用法自动转换时使用的。
|
||||
if (wrap === false) {
|
||||
@ -74,6 +82,7 @@ export default class Wrapper extends React.Component<WrapperProps, object> {
|
||||
className
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
{...buildTestId(testid)}
|
||||
>
|
||||
{this.renderBody()}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user