Merge branch 'pre-release' into master

This commit is contained in:
jiatianqi 2022-09-27 10:26:52 +08:00
commit 2803ad0a6a
39 changed files with 1919 additions and 141 deletions

View File

@ -1,6 +1,6 @@
{
"name": "amis-editor-core",
"version": "5.2.0-beta.61",
"version": "5.2.0-beta.74",
"description": "amis 可视化编辑器",
"main": "lib/index.min.js",
"types": "lib/index.d.ts",

View File

@ -1,7 +1,7 @@
.ae-ClassNamePicker-popover {
padding: 10px;
width: 610px;
max-height: 400px;
max-height: calc(100% - 400px);
overflow: auto;
&.ae-PopOver--leftBottomLeftTop {

View File

@ -89,7 +89,8 @@
}
@mixin panel-sm-content {
--ColorPicker-fontSize: var(--fontSizeBase);
--Form-fontSize: #{$Editor-right-panel-font-size};
--ColorPicker-fontSize: var($Editor-right-panel-font-size);
--fontSizeBase: #{$Editor-right-panel-font-size};
--Form-item-fontSize: #{$Editor-right-panel-font-size};
--Button--md-fontSize: #{$Editor-right-panel-font-size};

View File

@ -280,7 +280,7 @@ $category-2-height: px2rem(32px);
// tab导航
ul[role='tablist'],
&-links {
margin: 0 0 -1px 0;
margin: 0;
flex: 0;
border-bottom: 1px solid #d4d6d9;
display: flex;
@ -354,8 +354,9 @@ $category-2-height: px2rem(32px);
position: absolute;
width: 100%;
padding: 0;
overflow-y: overlay;
overflow-y: overlay !important;
@include minScrollBar();
margin-top: -1px;
}
div.ae-switch-more-flex {

View File

@ -19,10 +19,63 @@
@include flexBox();
.ae-ApiControl-input {
background: var(--Form-input-bg);
border: var(--Form-input-borderWidth) solid var(--Form-input-borderColor);
border-radius: var(--Form-input-borderRadius);
line-height: var(--Form-input-lineHeight);
padding: var(--Form-input-paddingY) var(--Form-input-paddingX);
font-size: var(--Form-input-fontSize);
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
flex: 1;
margin-right: #{px2rem(10px)};
max-width: calc(100% - 52px);
height: var(--Button--sm-height);
& > input {
flex-basis: 5rem;
flex-grow: 1;
outline: 0;
background: transparent;
border: 0;
color: var(--Form-input-color);
width: 100%;
height: calc(var(--Form-input-lineHeight) * var(--Form-input-fontSize));
}
}
}
&-highlight {
width: 100%;
max-width: calc(100% - var(--fontSizeLg));
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
&-tag {
display: inline-block;
background: #007bff;
padding: 3px 5px;
margin: 0 1px;
color: #fff;
font-size: 12px;
line-height: 14px;
height: 20px;
border-radius: #{px2rem(4px)};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 90%;
}
}
&-icon {
width: var(--fontSizeLg) !important;
height: var(--fontSizeLg) !important;
}
&-dialog {
&-body {
@ -58,3 +111,12 @@
}
}
}
.ae-ApiControl-PickerBtn {
padding: 0;
&:hover > svg path {
stroke: var(--primary);
color: var(--primary);
}
}

View File

@ -0,0 +1,98 @@
.ae-DataBindingList {
display: flex;
flex-flow: column nowrap;
justify-content: flex-start;
align-items: stretch;
height: px2rem(350px);
border: 1px solid rgba(232, 233, 235, 1);
border-radius: px2rem(4px);
overflow: scroll;
&-hint {
width: 100%;
line-height: 3;
text-align: center;
color: var(--text--muted-color);
}
&-searchBox {
width: auto;
padding: #{px2rem(12px)};
& > div {
width: 100% !important;
}
}
&-body {
@include minScrollBar();
flex: 1;
overflow-x: hidden;
overflow-y: auto;
}
&-collapse {
border: none;
background: #f7f7f9;
&-title {
display: block !important;
padding: #{px2rem(5px)} #{px2rem(12px)};
background: transparent !important;
font-size: var(--fontSizeSm);
font-weight: bold;
position: relative;
.expandIcon {
font-size: var(--fontSizeSm);
line-height: var(--fontSizeXl);
transform-origin: #{px2rem(7px)} #{px2rem(9px)};
transition: transform 0.2s;
position: absolute;
right: #{px2rem(6px)};
margin-top: 3px;
}
}
&-body {
background: #fff;
color: #303540;
> div {
padding: 5px 0;
}
}
}
&-item {
display: flex;
flex-direction: row;
align-items: baseline;
cursor: pointer;
padding: 0 var(--gap-xl); // 和标题对齐不好看加个缩进
height: px2rem(32px);
line-height: px2rem(32px);
color: #303540;
font-weight: 400;
span {
flex-grow: 1;
}
&:hover {
background: var(--Tree-item-onHover-bg);
}
&.is-active {
color: var(--primary);
background: var(--Tree-item-onHover-bg);
}
}
&-empty {
color: #b4b6ba;
padding-top: px2rem(10px);
text-align: center;
vertical-align: middle;
}
}

View File

@ -9,11 +9,12 @@
}
&-header {
position: fixed;
top: #{px2rem(45px)};
top: #{px2rem(48px)};
width: 100%;
padding: #{px2rem(12px)};
background: #fff;
z-index: 1;
margin-top: 1px;
.add-event-dropdown {
button {
top: 44px;
@ -75,6 +76,14 @@
}
}
}
&-desc {
margin: #{px2rem(12px)};
color: #84868c;
button > svg {
width: #{px2rem(12px)};
height: #{px2rem(12px)};
}
}
&:last-child {
.event-item-header {
border-bottom: #{px2rem(1px)} solid #d4d6d9;
@ -87,7 +96,9 @@
@include flexBox(column, flex-start);
@include minScrollBar();
margin: 0;
padding: #{px2rem(13px)} #{px2rem(8px)} 0;
padding-left: #{px2rem(12px)};
padding-right: #{px2rem(12px)};
padding-top: #{px2rem(12px)};
background: #ffffff;
list-style-type: none;
.ae-option-control-item {

View File

@ -0,0 +1,64 @@
.ae-FeatureControl {
&-features {
margin: 0;
padding: 0;
}
&Item {
display: flex;
height: 30px;
margin-bottom: 12px;
:not(:last-child) {
margin-right: px2rem(8px);
}
&-go {
flex-grow: 1;
}
&-label {
flex-grow: 1;
height: px2rem(32px);
display: block;
line-height: px2rem(32px);
padding: 0 px2rem(8px);
border: var(--Form-input-borderWidth) solid var(--Form-input-borderColor);
border-radius: var(--Form-input-borderRadius);
text-align: center;
}
&-action {
padding: 0 6px;
svg {
width: px2rem(16px);
height: px2rem(16px);
fill: #000;
}
&:hover {
svg {
fill: $Editor-theme;
}
}
}
}
&-action {
display: block;
width: 100%;
&--btn {
width: 100%;
border-color: $Editor-theme;
color: $Editor-theme;
}
&--menus {
width: calc(100% - 12px);
margin-left: 6px;
text-align: center;
}
}
}

View File

@ -17,6 +17,12 @@
}
div[data-role='form-item'] {
&.ae-ExtendMore {
> label {
flex: 0;
padding: 0;
}
}
> label {
line-height: 32px;
margin: 0;
@ -134,3 +140,7 @@
height: px2rem(32px);
}
}
.form-item-gap {
margin-bottom: var(--Form-item-gap);
}

View File

@ -69,7 +69,7 @@
&.is-clearable {
> div:first-child {
max-width: calc(100% - 20px); // 避免表达式内容太长撑开面板
max-width: calc(100% - 24px); // 避免表达式内容太长撑开面板
> div,
span {

View File

@ -0,0 +1,29 @@
.ae-GoConfig {
height: 32px;
line-height: 32px;
position: relative;
background-color: #fff;
text-align: center;
font-size: $Editor-right-panel-font-size;
border: 1px solid #e6e6e8;
border-radius: $Editor-borderRadius;
&-trigger {
display: none;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba($color: #000000, $alpha: .4);
color: #fff;
cursor: pointer;
}
&:hover {
.ae-GoConfig-trigger {
display: block;
}
}
}

View File

@ -0,0 +1,13 @@
.inputFile-apiControl {
margin-top: 30px;
.ApiControl {
margin-bottom: 0;
&-header {
position: absolute;
right: 0;
top: -25px;
}
}
}

View File

@ -58,6 +58,12 @@
flex: 1;
margin: 0;
margin-right: #{px2rem(12px)};
input {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&-dropdown i {

View File

@ -0,0 +1,83 @@
.ae-TimelineItemControl {
&-header {
@include flexBox();
width: 100%;
height: #{px2rem(24px)};
margin-bottom: #{px2rem(12px)};
}
&-content {
@include flexBox(column, flex-start);
margin: 0;
padding: 0;
.ae-TimelineItemControlItem {
display: block;
width: 100%;
&-input-title {
flex: 1;
margin-left: #{px2rem(20px)};
margin-right: #{px2rem(44px)};
input {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&-input {
flex: 1;
margin: 0;
margin-right: #{px2rem(12px)};
}
&-Main {
@include flexBox();
width: 100%;
background: #fff;
padding-bottom: px2rem(12px);
}
&--dragging {
height: 0 !important;
padding: 0;
border-top: 2px solid var(--primary);
overflow: hidden;
background: #e9effd;
}
&-dragBar {
display: inline-flex;
margin-left: 0;
margin-right: var(--gap-sm);
cursor: move;
color: #8c8c8c;
}
&-dropdown i {
margin-right: 0px;
}
}
.ae-TimelineItemControlItem-inputDate {
margin-bottom: 0;
}
}
&-border {
background-color: #e5e5e5;
width: 100%;
height: 1px;
margin-top: 12px;
margin-bottom: 12px;
}
&-footer > * {
width: calc(50% - #{px2rem(6px)});
&:first-child {
margin-right: px2rem(12px);
}
}
}

View File

@ -26,8 +26,13 @@
@import './control/formula-control';
@import './control/dateshortcut-control';
@import './control/badge-control';
@import './control/go-config';
@import './control/feature-control';
@import './control/databinding-control';
@import './control/event-action';
@import './control/timeline_item_control';
@import './control/tree_option_control';
@import './control/_inpupt-file';
/* 样式控件 */
@import './style-control/box-model';
@ -1053,55 +1058,26 @@
.ae-Region-placeholder {
display: none;
text-align: center;
color: var(--text--muted-color);
user-select: none;
text-align: center;
text-transform: uppercase;
border: 1px dashed rgb(206, 208, 211);
background: rgba(10, 19, 37, 0.05);
// &:first-child {
// position: relative;
// display: flex;
// flex: 1;
// flex-direction: column;
// justify-content: center;
// min-width: 60px;
// padding: 0 5px;
// -webkit-user-select: none;
// user-select: none;
// text-align: center;
// text-transform: uppercase;
// color: var(--text--muted-color);
// // border: 1px dashed rgb(206, 208, 211);
// background: rgba(10, 19, 37, 0.05);
// }
&:first-child {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
}
[data-region] {
position: relative;
min-height: 34px;
&:empty {
min-width: 20px;
&:before {
height: 100%;
content: attr(data-region-placeholder);
position: relative;
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
padding: 0 5px;
-webkit-user-select: none;
user-select: none;
text-align: center;
text-transform: uppercase;
color: rgb(108, 113, 124);
border: 1px dashed rgb(206, 208, 211);
background: rgba(10, 19, 37, 0.05);
}
}
// &.is-region-active {
// min-height: 34px;
// }
&.is-dragenter {
background-color: #fff;
}
@ -1505,10 +1481,27 @@ div.ae-DragImage {
.ae-ApiSample {
min-width: 200px;
max-height: 300px;
&-desc {
font-size: var(--fontSizeSm);
display: inline-block;
margin-top: #{px2rem(5px)};
color: #84868c;
}
&-icon {
--Remark-onHover-bg: #{$Editor-theme-color};
& > i {
border: none;
padding: #{px2rem(10px)};
border-radius: #{px2rem(3px)};
}
}
> pre {
overflow: auto;
border: 1px solid #999;
page-break-inside: avoid;
display: block;
padding: 9.5px;
@ -1518,12 +1511,13 @@ div.ae-DragImage {
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f7f7f9;
border-radius: #{$Editor-borderRadius};
border: none;
> code {
white-space: pre;
color: #151a26;
}
}
@ -1565,3 +1559,51 @@ div.ae-DragImage {
margin-right: 0;
}
}
.ae-Scaffold-Modal {
width: px2rem(700px);
@include panel-sm-content();
.ae-Steps {
margin: auto;
max-width: px2rem(350px);
--Steps-title-fontsize: #{px2rem(14px)};
&-Icon {
width: px2rem(22px) !important;
height: px2rem(22px) !important;
font-size: px2rem(12px) !important;
}
}
&-Tabs {
--Tabs-linkFontSize: #{px2rem(12px)};
}
}
.ae-Button--link {
display: inline-flex;
align-items: center;
padding: 0 !important;
svg {
width: 12px;
margin-right: 4px !important;
}
}
.ae-Fields-Setting {
&-Item {
display: flex;
height: px2rem(32px);
margin-bottom: 12px;
padding: 0 px2rem(8px);
border: var(--Form-input-borderWidth) solid var(--Form-input-borderColor);
border-radius: var(--Form-input-borderRadius);
&-label {
flex-grow: 1;
line-height: px2rem(30px);
}
}
}

View File

@ -0,0 +1,598 @@
/**
* API数据源处理器
*/
import {Schema, toast} from 'amis';
import {
DSBuilder,
DSFeature,
DSFeatureType,
DSGrain,
registerDSBuilder
} from './DSBuilder';
import cloneDeep from 'lodash/cloneDeep';
import {getEnv} from 'mobx-state-tree';
import {ButtonSchema} from 'amis/lib/renderers/Action';
import {FormSchema, SchemaCollection, SchemaObject} from 'amis/lib/Schema';
import type {DSSourceSettingFormConfig} from './DSBuilder';
import {getSchemaTpl, tipedLabel} from '../tpl';
import {EditorNodeType} from '../store/node';
class APIBuilder extends DSBuilder {
public static type = 'api';
name = '接口';
order = 0;
public match = (value: any, schema?: SchemaObject) => {
// https://aisuda.bce.baidu.com/amis/zh-CN/docs/types/api
if (
(typeof value === 'string' &&
/^(get|post|put|delete|option):/.test(value)) ||
(typeof value === 'object' && value.url)
) {
return true;
}
return false;
};
public static accessable = (controlType: string, propKey: string) => {
return true;
};
public features: Array<DSFeatureType> = [
'List',
'Insert',
'View',
'Edit',
'Delete',
'BulkEdit',
'BulkDelete',
'Import',
'Export',
'SimpleQuery',
'FuzzyQuery'
];
public makeSourceSettingForm(
config: DSSourceSettingFormConfig
): SchemaObject[] {
let {name, label, feat, inCrud, inScaffold} = config;
if (['Import', 'Export', 'SimpleQuery', 'FuzzyQuery'].includes(feat)) {
return [];
}
label =
label ??
(inCrud && feat !== 'List' ? DSFeature[feat].label + '接口' : '接口');
name = name ?? (inScaffold ? DSFeature[feat].value + 'Api' : 'api');
let sampleBuilder = null;
let apiDesc = null;
switch (feat) {
case 'Insert':
(label as any) = tipedLabel(
label,
`用来保存数据, 表单提交后将数据传入此接口。 <br/>
(data中有数据)<br/>
${JSON.stringify({status: 0, msg: '', data: {}}, null, '<br/>')}`
);
break;
case 'List':
(label as any) = tipedLabel(
label,
`接口响应体要求:<br/>
${JSON.stringify(
{status: 0, msg: '', items: {}, page: 0, total: 0},
null,
'<br/>'
)}`
);
break;
}
return [
getSchemaTpl('apiControl', {
label,
name,
sampleBuilder,
apiDesc
})
]
.concat(
feat === 'Edit' && !inCrud
? getSchemaTpl('apiControl', {
label: tipedLabel(
'初始化接口',
`接口响应体要求:<br/>
${JSON.stringify({status: 0, msg: '', data: {}}, null, '<br/>')}`
),
name: 'initApi'
})
: null
)
.concat(
feat === 'List' && inCrud && inScaffold
? this.makeFieldsSettingForm({
feat,
setting: true
})
: null
)
.filter(Boolean);
}
public async getContextFileds(config: {
schema: any;
sourceKey: string;
feat: DSFeatureType;
}) {
return config.schema.__fields;
}
public async getAvailableContextFileds(
config: {
schema: any;
sourceKey: string;
feat: DSFeatureType;
},
target: EditorNodeType
) {
// API类目前没有增加API中心的出参入参后可以在这里提供绑定字段
// return {
// type: 'ae-SimpleDataBindingPanel',
// fields: [
// {
// label: '可用字段',
// children: [
// {label: '名称', value: 'name'},
// {label: '年级', value: 'grade'}
// ]
// }
// ]
// } as any;
}
onFieldsInit(value: any, form: any) {
this.features.forEach(feat => {
const key = `${DSFeature[feat].value}Fields`;
const currentData = form.getValueByName(key);
const result = cloneDeep(value || []).map((field: any) => {
const exist = currentData?.find((f: any) => f.name === field.name);
return {
...field,
checked: exist ? exist.checked : true
};
});
form.setValueByName(key, result);
});
}
public makeFieldsSettingForm(config: {
sourceKey?: string;
feat: DSFeatureType;
inCrud?: boolean;
setting?: boolean;
inScaffold?: boolean;
}) {
let {sourceKey, feat, inCrud, setting, inScaffold} = config;
if (
inScaffold === false ||
['Import', 'Export', 'FuzzyQuery'].includes(feat)
) {
return [];
}
sourceKey = sourceKey ?? `${DSFeature[feat].value}Api`;
const key = setting ? '__fields' : `${DSFeature[feat].value}Fields`;
const hasInputType =
['Edit', 'Insert'].includes(feat) || (inCrud && feat === 'List');
const hasType = ['View', 'List'].includes(feat);
return ([] as any)
.concat(
inCrud && feat !== 'List'
? this.makeSourceSettingForm({
feat,
inScaffold,
inCrud
})
: null
)
.concat([
{
type: 'combo',
className: 'mb-0 ae-Fields-Setting',
joinValues: false,
name: key,
label: inCrud ? `${DSFeature[feat].label}字段` : '字段',
multiple: true,
draggable: true,
addable: false,
removable: false,
itemClassName: 'ae-Fields-Setting-Item',
// CRUD的脚手架面板基于现有字段进行选择
hidden: setting || !inCrud || ['Delete', 'BulkDelete'].includes(feat),
items: {
type: 'container',
body: [
{
name: 'checked',
label: false,
mode: 'inline',
className: 'm-0 ml-1',
type: 'checkbox'
},
{
type: 'tpl',
className: 'ae-Fields-Setting-Item-label',
tpl: '${label}'
}
]
}
},
{
type: 'input-table',
label: '字段',
className: 'mb-0',
name: key,
// 非crud都是定义字段的模式只有crud有统一定义字段因此是选择字段
visible: setting ?? !inCrud,
removable: true,
columnsTogglable: false,
needConfirm: false,
onChange: (value: any, oldValue: any, model: any, form: any) =>
this.onFieldsInit(value, form),
columns: [
{
type: 'switch',
name: 'checked',
value: true,
label: '隐藏,默认选中',
visible: false
},
{
type: 'input-text',
name: 'label',
label: '标题'
},
{
type: 'input-text',
name: 'name',
label: '绑定字段'
},
{
type: 'select',
name: 'type',
label: '类型',
visible: hasType,
value: 'tpl',
options: [
{
value: 'tpl',
label: '文本',
typeKey: 'tpl'
},
{
value: 'image',
label: '图片',
typeKey: 'src'
},
{
value: 'date',
label: '日期',
typeKey: 'value'
},
{
value: 'progress',
label: '进度',
typeKey: 'value'
},
{
value: 'status',
label: '状态',
typeKey: 'value'
},
{
value: 'mapping',
label: '映射',
typeKey: 'value'
}
],
autoFill: {
typeKey: '${typeKey}'
}
},
{
type: 'select',
name: 'inputType',
label: '输入类型',
visible: hasInputType,
value: 'input-text',
options: [
{
label: '输入框',
value: 'input-text'
},
{
label: '多行文本',
value: 'textarea'
},
{
label: '数字输入',
value: 'input-number'
},
{
label: '单选框',
value: 'radios'
},
{
label: '勾选框',
value: 'checkbox'
},
{
label: '复选框',
value: 'checkboxes'
},
{
label: '下拉框',
value: 'select'
},
{
label: '开关',
value: 'switch'
},
{
label: '日期',
value: 'input-date'
},
{
label: '表格',
value: 'input-table'
},
{
label: '文件上传',
value: 'input-file'
},
{
label: '图片上传',
value: 'input-image'
},
{
label: '富文本编辑器',
value: 'input-rich-text'
}
]
}
]
},
{
type: 'group',
visible: setting ?? !inCrud,
label: '',
body: [
{
type: 'grid',
columns: [
{
body: [
{
type: 'button',
label: '添加字段',
target: key,
className: 'ae-Button--link',
level: 'link',
icon: 'plus',
actionType: 'add'
}
]
},
{
columnClassName: 'text-right',
body: [
{
type: 'button',
label: '基于接口自动生成字段',
visible: feat === 'Edit' || feat === 'List',
className: 'ae-Button--link',
level: 'link',
// className: 'm-t-xs m-b-xs',
// 列表 或者 不在CRUD中的查看接口等
onClick: async (e: Event, props: any) => {
const data = props.data;
const schemaFilter = getEnv(
(window as any).editorStore
).schemaFilter;
const apiKey =
feat === 'Edit' && !inCrud ? 'initApi' : sourceKey;
let api: any = data[apiKey!];
// 主要是给爱速搭中替换 url
if (schemaFilter) {
api = schemaFilter({
api
}).api;
}
if (!api) {
toast.warning('请先填写接口');
}
const result = await props.env.fetcher(api, data);
let autoFillKeyValues: Array<any> = [];
let itemExample;
if (feat === 'List') {
const items = result.data?.rows || result.data?.items;
itemExample = items?.[0];
} else {
itemExample = result.data;
}
if (itemExample) {
Object.entries(itemExample).forEach(
([key, value]) => {
autoFillKeyValues.push({
label: key,
type: 'tpl',
inputType:
typeof value === 'number'
? 'input-number'
: 'input-text',
name: key
});
}
);
props.formStore.setValues({
[key]: autoFillKeyValues
});
this.onFieldsInit(autoFillKeyValues, props.formStore);
} else {
toast.warning(
'API返回格式不正确请查看接口响应格式要求'
);
}
}
}
]
}
]
}
]
}
]) as SchemaObject[];
}
public async makeFieldFilterSetting(config: {
/** 数据源字段名 */
sourceKey: string;
schema: any;
fieldName: string;
}) {
return [];
}
public resolveSourceSchema(config: {
schema: SchemaObject;
setting: any;
name?: string;
feat?: DSFeatureType;
inCrud?: boolean;
}): void {
let {name, setting, schema, feat} = config;
name = name ?? 'api';
// @ts-ignore
schema[name] = setting[feat ? `${DSFeature[feat].value}Api` : 'api'];
// form中需要初始化接口和编辑接口
if (feat === 'Edit') {
(schema as FormSchema).initApi = setting.initApi;
}
}
public resolveViewSchema(config: {
setting: any;
feat?: DSFeatureType;
}): SchemaObject[] {
let {setting, feat = 'Edit'} = config;
const fields = setting[`${DSFeature[feat].value}Fields`] || [];
return fields
.filter((i: any) => i.checked)
.map((field: any) => ({
type: field.type,
[field.typeKey || 'value']: '${' + field.name + '}'
}));
}
public resolveTableSchema(config: {schema: any; setting: any}): void {
let {schema, setting} = config;
const fields = setting.listFields.filter((i: any) => i.checked) || [];
schema.columns = this.makeTableColumnsByFields(fields);
}
public makeTableColumnsByFields(fields: any[]) {
return fields.map((field: any) => ({
type: field.type,
title: field.label,
key: field.name,
[field.typeKey || 'value']: '${' + field.name + '}'
}));
}
public resolveCreateSchema(config: {
schema: FormSchema;
setting: any;
feat: 'Insert' | 'Edit' | 'BulkEdit';
name?: string;
inCrud?: boolean;
inScaffold?: boolean;
}): void {
let {schema, setting, feat, name} = config;
const fields = setting[`${DSFeature[feat].value}Fields`] || [];
// @ts-ignore
schema[name ?? 'api'] = setting[DSFeature[feat].value + 'Api'];
schema.initApi = setting['initApi'];
schema.body = fields
.filter((i: any) => i.checked)
.map((field: any) => ({
type: field.inputType,
name: field.name,
label: field.label
}));
}
public resolveDeleteSchema(config: {
schema: ButtonSchema;
setting: any;
feat: 'BulkDelete' | 'Delete';
name?: string | undefined;
}) {
const {schema, setting, feat} = config;
schema.onEvent = Object.assign(schema.onEvent ?? {}, {
click: {
actions: []
}
});
const api = {
...(setting[`${DSFeature[feat].value}Api`] || {})
};
if (feat === 'Delete') {
api.data = {
id: '${item.id}'
};
} else {
api.data = {
ids: '${ARRAYMAP(selectedItems, item=> item.id)}'
};
}
schema.onEvent.click.actions.push({
actionType: 'ajax',
args: {api}
});
}
public resolveSimpleFilterSchema(config: {setting: any}) {
const {setting} = config;
const fields = setting.simpleQueryFields || [];
return fields
.filter((i: any) => i.checked)
.map((field: any) => ({
type: field.inputType,
name: field.name,
label: field.label
}));
}
public resolveAdvancedFilterSchema(config: {setting: any}) {
return;
}
}
registerDSBuilder(APIBuilder);

View File

@ -0,0 +1,372 @@
/**
* amis中的扩展数据源
*/
import {ButtonSchema} from 'amis/lib/renderers/Action';
import {CRUD2Schema} from 'amis/lib/renderers/CRUD2';
import {FormSchema, SchemaCollection, SchemaObject} from 'amis/lib/Schema';
import {EditorNodeType} from '../store/node';
/**
* schema从后端来
*/
export enum DSBehavior {
create = 'create',
view = 'view',
update = 'update',
table = 'table',
filter = 'filter'
}
export interface DSField {
value: string;
label: string;
[propKey: string]: any;
}
export interface DSFieldGroup {
value: string;
label: string;
children: DSField[];
[propKey: string]: any;
}
/**
*
*/
export enum DSGrain {
entity = 'entity',
list = 'list',
piece = 'piece'
}
export const DSFeature = {
List: {
value: 'list',
label: '列表'
},
Insert: {
value: 'insert',
label: '新增'
},
View: {
value: 'view',
label: '详情'
},
Edit: {
value: 'edit',
label: '编辑'
},
Delete: {
value: 'delete',
label: '删除'
},
BulkEdit: {
value: 'bulkEdit',
label: '批量编辑'
},
BulkDelete: {
value: 'bulkDelete',
label: '批量删除'
},
Import: {
value: 'import',
label: '导入'
},
Export: {
value: 'export',
label: '导出'
},
SimpleQuery: {
value: 'simpleQuery',
label: '简单查询'
},
FuzzyQuery: {
value: 'fuzzyQuery',
label: '模糊查询'
},
AdvancedQuery: {
value: 'advancedQuery',
label: '高级查询'
}
};
export type DSFeatureType = keyof typeof DSFeature;
export interface DSSourceSettingFormConfig {
/** 数据源字段名 */
name?: string;
/** 数据源字段标题 */
label?: string;
/** 所需要配置的数据粒度 */
grain?: DSGrain;
/** 数据源所被使用的功能场景 */
feat: DSFeatureType;
/** 是否是在CRUD场景下有的数据源在CRUD中可以统一设置 */
inCrud?: boolean;
/** 是否在脚手架中 */
inScaffold?: boolean;
}
/**
*
*/
export abstract class DSBuilder {
/**
*
*/
public static type: string;
public name: string;
// 数字越小排序越靠前
public order: number;
/**
* schema运行前转换
*/
public static schemaFilter?: (schema: any) => any;
/**
* 使
*/
public static accessable: (controlType: string, propKey: string) => boolean;
public features: Array<keyof typeof DSFeature>;
/**
* schema配置状态
*/
public abstract match(value: any, schema?: SchemaObject): boolean;
/**
*
*/
public abstract makeSourceSettingForm(
config: DSSourceSettingFormConfig
): SchemaObject[];
public abstract makeFieldsSettingForm(config: {
/** 数据源字段名 */
sourceKey?: string;
feat: DSFeatureType;
inCrud?: boolean;
inScaffold?: boolean;
/** 初次设置字段还是选择字段 */
setting?: boolean;
}): SchemaObject[];
/**
*
*/
public abstract makeFieldFilterSetting(config: {
/** 数据源字段名 */
sourceKey: string;
schema: any;
fieldName: string;
}): Promise<SchemaObject[]>;
/**
* schema生成
*/
abstract resolveSourceSchema(config: {
/** schema */
schema: SchemaObject;
/** 数据源配置结果 */
setting: any;
/** 数据源字段名 */
name?: string;
feat?: DSFeatureType;
/** 是否是在CRUD场景下有的数据源在CRUD中可以统一设置 */
inCrud?: boolean;
inScaffold?: boolean;
}): void;
/**
* schema生成
*/
abstract resolveDeleteSchema(config: {
schema: ButtonSchema;
setting: any;
feat: 'BulkDelete' | 'Delete';
name?: string;
}): any;
/**
* schema
*/
abstract resolveCreateSchema(config: {
/** schema */
schema: FormSchema;
/** 脚手架配置数据 */
setting: any;
feat: 'Insert' | 'Edit' | 'BulkEdit';
/** 数据源字段名 */
name?: string;
/** 是否是在CRUD场景下有的数据源在CRUD中可以统一设置 */
inCrud?: boolean;
}): void;
/**
*
*/
abstract resolveTableSchema(config: {
/** schema */
schema: CRUD2Schema;
/** 脚手架配置数据 */
setting: any;
/** 数据源字段名 */
name?: string;
/** 是否是在CRUD场景下有的数据源在CRUD中可以统一设置 */
inCrud?: boolean;
}): void;
/**
*
*/
abstract resolveViewSchema(config: {
/** 脚手架配置数据 */
setting: any;
feat?: DSFeatureType;
}): SchemaObject[];
abstract resolveSimpleFilterSchema(config: {setting: any}): SchemaObject[];
abstract resolveAdvancedFilterSchema(config: {
setting: any;
}): SchemaObject | void;
abstract makeTableColumnsByFields(fields: any[]): SchemaObject[];
/**
* 使
*/
abstract getContextFileds(config: {
schema: any;
sourceKey: string;
feat: DSFeatureType;
}): Promise<DSField[] | void>;
/**
* 使
*/
abstract getAvailableContextFileds(
config: {
schema: any;
sourceKey: string;
feat: DSFeatureType;
},
target: EditorNodeType
): Promise<SchemaCollection | void>;
}
/**
*
*/
const __builders: {
[key: string]: any;
} = {};
export const registerDSBuilder = (builderKClass: any) => {
__builders[builderKClass.type] = builderKClass;
};
/**
* 便
*/
export class DSBuilderManager {
/** 所有可用的数据源构造器实例 */
builders: {
[key: string]: DSBuilder;
} = {};
get builderNum() {
return Object.keys(this.builders).length;
}
constructor(type: string, propKey: string) {
Object.values(__builders)
.filter(builder => builder.accessable?.(type, propKey) ?? true)
.forEach(Builder => {
this.builders[Builder.type] = new Builder();
});
}
resolveBuilderBySetting(setting: any) {
return this.builders[setting.dsType] || Object.values(this.builders)[0];
}
resolveBuilderBySchema(schema: any, propKey: string) {
const builders = Object.values(this.builders);
return (
builders.find(builder => builder.match(schema[propKey])) || builders[0]
);
}
getDefaultBuilderName() {
// 先返回第一个之后可以加一些order之类的
const builderOptions = Object.entries(this.builders)
.map(([key, builder]) => {
return {
value: key,
order: builder.order
};
})
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
return builderOptions[0].value;
}
getDSSwitch(setting: any = {}) {
const multiSource = this.builderNum > 1;
const builderOptions = Object.entries(this.builders).map(
([key, builder]) => ({
label: builder.name,
value: key,
order: builder.order
})
);
builderOptions.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
return {
type: 'radios',
label: '数据来源',
name: 'dsType',
visible: multiSource,
selectFirst: true,
options: builderOptions,
...setting
};
}
// getDSSwitchFormForPanel(
// propKey: string,
// label: string
// ) {
// return Object.keys(this.builders).length > 1 ? {
// type: Object.keys(this.builders).length > 3 ? 'select' : 'button-group-select',
// options: Object.keys(this.builders).map(name => ({
// label: name,
// value: name
// })),
// name: propKey,
// label: label,
// pipeIn: (value: string) => {
// const builders = Object.entries(this.builders);
// return (builders.find(([, builder]) => {
// return builder.match(value);
// }) || builders[0])[0];
// },
// pipeOut: (value: string) => {
// return this.builders[value].defaultSchema || {};
// }
// } : null;
// }
collectFromBuilders(
callee: (builder: DSBuilder, builderName: string) => any
) {
return Object.entries(this.builders).map(([name, builder]) => {
return callee(builder, name);
});
}
}

View File

@ -97,10 +97,6 @@ export class RegionWrapper extends React.Component<RegionWrapperProps> {
wrapper.setAttribute('data-region', region);
wrapper.setAttribute('data-region-host', id);
wrapper.setAttribute(
'data-region-placeholder',
this.props.placeholder || this.props.label
);
rendererName && wrapper.setAttribute('data-renderer', rendererName);
}
@ -108,7 +104,9 @@ export class RegionWrapper extends React.Component<RegionWrapperProps> {
return (
<EditorNodeContext.Provider value={this.editorNode}>
{this.props.children}
<span className="ae-Region-placeholder" />
<span className="ae-Region-placeholder">
{this.props.placeholder || this.props.label}
</span>
</EditorNodeContext.Provider>
);
}

View File

@ -3,7 +3,7 @@ import {EditorManager} from '../manager';
import {EditorStoreType} from '../store/editor';
import {render, Modal, getTheme, Icon, Spinner, Button} from 'amis';
import {observer} from 'mobx-react';
import {autobind} from '../util';
import {autobind, isObject} from '../util';
import {createObject} from 'amis-core';
export interface SubEditorProps {
@ -12,8 +12,20 @@ export interface SubEditorProps {
theme?: string;
}
interface ScaffoldState {
step: number
}
@observer
export class ScaffoldModal extends React.Component<SubEditorProps> {
export class ScaffoldModal extends React.Component<SubEditorProps, ScaffoldState> {
constructor(props: SubEditorProps) {
super(props);
this.state = {
step: 0
};
}
@autobind
handleConfirm([values]: any) {
const store = this.props.store;
@ -32,21 +44,54 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
store.scaffoldForm?.callback(values);
store.closeScaffoldForm();
this.setState({step: 0});
}
buildSchema() {
const {store} = this.props;
const scaffoldFormContext = store.scaffoldForm!;
let body = scaffoldFormContext.controls ?? scaffoldFormContext.body;
if (scaffoldFormContext.stepsBody) {
body = [
{
type: 'steps',
name: '__steps',
className: 'ae-Steps',
steps: body.map((step, index) => ({
title: step.title,
value: index,
iconClassName: 'ae-Steps-Icon'
}))
},
...body.map((step, index) => ({
type: 'container',
visibleOn: `__step === ${index}`,
body: step.body
}))
]
}
let layout: object;
if (isObject(scaffoldFormContext.mode)) {
layout = scaffoldFormContext.mode as object;
} else {
layout = {
mode: scaffoldFormContext.mode || 'normal'
}
}
return {
type: 'form',
wrapWithPanel: false,
initApi: scaffoldFormContext.initApi,
api: scaffoldFormContext.api,
mode: scaffoldFormContext.mode || 'normal',
...layout,
wrapperComponent: 'div',
[scaffoldFormContext.controls ? 'controls' : 'body']:
scaffoldFormContext.controls ?? scaffoldFormContext.body
data: {
__step: 0
},
[scaffoldFormContext.controls ? 'controls' : 'body']: body,
};
// const {store} = this.props;
// const scaffoldFormContext = store.scaffoldForm;
@ -100,6 +145,32 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
this.amisScope = scoped;
}
@autobind
goToNextStep() {
// 不能更新props的data控制amis不重新渲染否则数据会重新初始化
const form = this.amisScope?.getComponents()[0].props.store;
const step = this.state.step + 1;
form.setValueByName('__step', step);
// 控制按钮
this.setState({
step
});
}
@autobind
goToPrevStep() {
// 不能更新props的data控制amis不重新渲染否则数据会重新初始化
const form = this.amisScope?.getComponents()[0].props.store;
const step = this.state.step - 1;
form.setValueByName('__step', step);
// 控制按钮
this.setState({
step
});
}
@autobind
async handleConfirmClick() {
const form = this.amisScope?.getComponents()[0];
@ -129,14 +200,25 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
}
}
@autobind
handleCancelClick() {
this.props.store.closeScaffoldForm();
this.setState({step: 0});
}
render() {
const {store, theme, manager} = this.props;
const scaffoldFormContext = store.scaffoldForm;
const cx = getTheme(theme || 'cxd').classnames;
const isStepBody = !! scaffoldFormContext?.stepsBody;
const isLastStep = isStepBody && this.state.step === scaffoldFormContext!.body.length - 1;
const isFirstStep = isStepBody && this.state.step === 0;
return (
<Modal
size={scaffoldFormContext?.size || 'md'}
contentClassName={scaffoldFormContext?.className}
show={!!scaffoldFormContext}
onHide={store.closeScaffoldForm}
closeOnEsc={!store.scaffoldFormBuzy}
@ -158,7 +240,10 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
render(
this.buildSchema(),
{
data: createObject(store.ctx, scaffoldFormContext?.value),
data: createObject(store.ctx, {
...(scaffoldFormContext?.value || {}),
__step: 0
}),
onValidate: scaffoldFormContext.validate,
scopeRef: this.scopeRef
},
@ -184,6 +269,28 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
) : null}
</div>
) : null}
{
isStepBody && !isFirstStep && (
<Button
level="primary"
onClick={this.goToPrevStep}
>
</Button>
)
}
{
isStepBody && !isLastStep && (
<Button
level="primary"
onClick={this.goToNextStep}
>
</Button>
)
}
{
(!isStepBody || isLastStep) && (
<Button
level="primary"
onClick={this.handleConfirmClick}
@ -191,7 +298,9 @@ export class ScaffoldModal extends React.Component<SubEditorProps> {
>
</Button>
<Button onClick={store.closeScaffoldForm}></Button>
)
}
<Button onClick={this.handleCancelClick}></Button>
</div>
</Modal>
);

View File

@ -87,6 +87,7 @@ export default class SearchPanel extends React.Component<
this.curInputBox = this.ref.current.childNodes[0].childNodes[0];
this.curInputBox.addEventListener('keyup', this.bindEnterEvent);
}
this.updateCurKeyword('');
}
componentWillUnmount() {
@ -101,7 +102,7 @@ export default class SearchPanel extends React.Component<
if (externalKeyword !== this.state.curKeyword) {
this.setState(
{
curKeyword: externalKeyword,
curKeyword: externalKeyword
},
() => {
this.groupedResultByKeyword(externalKeyword);
@ -122,7 +123,7 @@ export default class SearchPanel extends React.Component<
}
this.setState({
resultTags: curResultTags,
resultByTag: curResultByTag,
resultByTag: curResultByTag
});
}
}
@ -199,10 +200,9 @@ export default class SearchPanel extends React.Component<
if (isString(item) && regular && regular.test(item)) {
// 兼容字符串类型
curSearchResult.push(item);
}
else if (
!keywords
|| ['name', 'description', 'scaffold.type'].some(
} else if (
!keywords ||
['name', 'description', 'scaffold.type'].some(
key => item[key] && regular && regular.test(item[key])
)
) {
@ -216,8 +216,7 @@ export default class SearchPanel extends React.Component<
curSearchResultByTag[tag] = grouped[tag] || [];
curSearchResultByTag[tag].push(item);
});
}
else {
} else {
curSearchResult.push(item);
}
}
@ -226,7 +225,7 @@ export default class SearchPanel extends React.Component<
// 更新当前搜索结果数据(备注: 附带重置功能)
this.setState({
searchResult: curSearchResult,
searchResultByTag: curSearchResultByTag,
searchResultByTag: curSearchResultByTag
});
}
@ -452,13 +451,8 @@ export default class SearchPanel extends React.Component<
render() {
const {allResult, closeAutoComplete, immediateChange} = this.props;
const {
resultTags,
curKeyword,
searchResult,
searchResultByTag,
visible
} = this.state;
const {resultTags, curKeyword, searchResult, searchResultByTag, visible} =
this.state;
const searchResultTags = searchResultByTag
? Object.keys(searchResultByTag)
: [];

View File

@ -19,6 +19,7 @@ import {CommonConfigWrapper} from './CommonConfigWrapper';
import {Schema} from 'amis/lib/types';
import type {DataScope} from 'amis-core';
import type {RendererConfig} from 'amis-core/lib/factory';
import {SchemaCollection} from 'amis/lib/Schema';
// 创建 Node Store 并构建成树
export function makeWrapper(
@ -69,6 +70,7 @@ export function makeWrapper(
});
this.editorNode!.setRendererConfig(rendererConfig);
// 查找父数据域,将当前组件数据域追加上去,使其形成父子关系
if (
rendererConfig.storeType &&
!manager.dataSchema.hasScope(`${info.id}-${info.type}`)
@ -301,7 +303,7 @@ function SchemaFrom({
export function makeSchemaFormRender(
manager: EditorManager,
schema: {
body?: Array<any>;
body?: SchemaCollection;
controls?: Array<any>;
definitions?: any;
api?: any;
@ -309,7 +311,7 @@ export function makeSchemaFormRender(
justify?: boolean;
panelById?: string;
formKey?: string;
},
}
) {
const env = {...manager.env, session: 'schema-form'};
@ -330,9 +332,10 @@ export function makeSchemaFormRender(
});
}
// 每一层的面板数据不要共用
const curFormKey = `${id}-${node?.type}${schema.formKey ? '-': ''}${schema.formKey ? schema.formKey: ''}`;
const curFormKey = `${id}-${node?.type}${schema.formKey ? '-' : ''}${
schema.formKey ? schema.formKey : ''
}`;
return (
<SchemaFrom
@ -712,6 +715,7 @@ export function renderThumbToGhost(
const html =
thumbHost.innerHTML ||
'<div class="wrapper-sm b-a b-light m-b-sm">拖入占位</div>';
// bca-disable-line
ghost.innerHTML = html;
/* bca-enable */

View File

@ -1,10 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon/删除</title>
<g id="icon/删除" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g>
<rect id="矩形" x="0" y="0" width="14" height="14"></rect>
<path d="M9.25,1.1 C9.44329966,1.1 9.60457492,1.2371128 9.64187342,1.41938605 L9.65,1.5 L9.65,2.653 L12.8,2.653125 C12.9104569,2.653125 13,2.74266805 13,2.853125 L13,3.253125 C13,3.36358195 12.9104569,3.453125 12.8,3.453125 L11.15,3.453 L11.15,10.4974999 C11.15,11.7677548 10.163161,12.8075211 8.91431873,12.891963 L8.75,12.8974999 L5.25,12.8974999 C3.97974508,12.8974999 2.93997876,11.9106609 2.85553687,10.6618186 L2.85,10.4974999 L2.849,3.453 L1.2,3.453125 C1.08954305,3.453125 1,3.36358195 1,3.253125 L1,2.853125 C1,2.74266805 1.08954305,2.653125 1.2,2.653125 L4.349,2.653 L4.35,1.5 C4.35,1.30670034 4.4871128,1.14542508 4.66938605,1.10812658 L4.75,1.1 L9.25,1.1 Z M10.35,3.453 L3.649,3.453 L3.65,10.4974999 C3.65,11.3320635 4.28896152,12.0173897 5.10436739,12.0909612 L5.25,12.0974999 L8.75,12.0974999 C9.58456362,12.0974999 10.2698898,11.4585384 10.3434613,10.6431325 L10.35,10.4974999 L10.35,3.453 Z M4.81646861,4.99747011 L5.21643661,5.00252989 C5.32688472,5.00392711 5.41528793,5.09459567 5.41389071,5.20504378 L5.34937289,10.3050758 C5.34797567,10.4155239 5.25730711,10.5039271 5.146859,10.5025299 L4.746891,10.4974701 C4.63644289,10.4960729 4.54803967,10.4054043 4.5494369,10.2949562 L4.61395472,5.19492421 C4.61535194,5.0844761 4.7060205,4.99607289 4.81646861,4.99747011 Z M6.81646861,4.99747011 L7.21643661,5.00252989 C7.32688472,5.00392711 7.41528793,5.09459567 7.41389071,5.20504378 L7.34937289,10.3050758 C7.34797567,10.4155239 7.25730711,10.5039271 7.146859,10.5025299 L6.746891,10.4974701 C6.63644289,10.4960729 6.54803967,10.4054043 6.5494369,10.2949562 L6.61395472,5.19492421 C6.61535194,5.0844761 6.7060205,4.99607289 6.81646861,4.99747011 Z M8.81646861,4.99747011 L9.21643661,5.00252989 C9.32688472,5.00392711 9.41528793,5.09459567 9.41389071,5.20504378 L9.34937289,10.3050758 C9.34797567,10.4155239 9.25730711,10.5039271 9.146859,10.5025299 L8.746891,10.4974701 C8.63644289,10.4960729 8.54803967,10.4054043 8.5494369,10.2949562 L8.61395472,5.19492421 C8.61535194,5.0844761 8.7060205,4.99607289 8.81646861,4.99747011 Z M8.85,1.9 L5.15,1.9 L5.15,2.653 L8.85,2.653 L8.85,1.9 Z" id="形状结合" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
<svg viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M9.25,1.1 C9.44329966,1.1 9.60457492,1.2371128 9.64187342,1.41938605 L9.65,1.5 L9.65,2.653 L12.8,2.653125 C12.9104569,2.653125 13,2.74266805 13,2.853125 L13,3.253125 C13,3.36358195 12.9104569,3.453125 12.8,3.453125 L11.15,3.453 L11.15,10.4974999 C11.15,11.7677548 10.163161,12.8075211 8.91431873,12.891963 L8.75,12.8974999 L5.25,12.8974999 C3.97974508,12.8974999 2.93997876,11.9106609 2.85553687,10.6618186 L2.85,10.4974999 L2.849,3.453 L1.2,3.453125 C1.08954305,3.453125 1,3.36358195 1,3.253125 L1,2.853125 C1,2.74266805 1.08954305,2.653125 1.2,2.653125 L4.349,2.653 L4.35,1.5 C4.35,1.30670034 4.4871128,1.14542508 4.66938605,1.10812658 L4.75,1.1 L9.25,1.1 Z M10.35,3.453 L3.649,3.453 L3.65,10.4974999 C3.65,11.3320635 4.28896152,12.0173897 5.10436739,12.0909612 L5.25,12.0974999 L8.75,12.0974999 C9.58456362,12.0974999 10.2698898,11.4585384 10.3434613,10.6431325 L10.35,10.4974999 L10.35,3.453 Z M4.81646861,4.99747011 L5.21643661,5.00252989 C5.32688472,5.00392711 5.41528793,5.09459567 5.41389071,5.20504378 L5.34937289,10.3050758 C5.34797567,10.4155239 5.25730711,10.5039271 5.146859,10.5025299 L4.746891,10.4974701 C4.63644289,10.4960729 4.54803967,10.4054043 4.5494369,10.2949562 L4.61395472,5.19492421 C4.61535194,5.0844761 4.7060205,4.99607289 4.81646861,4.99747011 Z M6.81646861,4.99747011 L7.21643661,5.00252989 C7.32688472,5.00392711 7.41528793,5.09459567 7.41389071,5.20504378 L7.34937289,10.3050758 C7.34797567,10.4155239 7.25730711,10.5039271 7.146859,10.5025299 L6.746891,10.4974701 C6.63644289,10.4960729 6.54803967,10.4054043 6.5494369,10.2949562 L6.61395472,5.19492421 C6.61535194,5.0844761 6.7060205,4.99607289 6.81646861,4.99747011 Z M8.81646861,4.99747011 L9.21643661,5.00252989 C9.32688472,5.00392711 9.41528793,5.09459567 9.41389071,5.20504378 L9.34937289,10.3050758 C9.34797567,10.4155239 9.25730711,10.5039271 9.146859,10.5025299 L8.746891,10.4974701 C8.63644289,10.4960729 8.54803967,10.4054043 8.5494369,10.2949562 L8.61395472,5.19492421 C8.61535194,5.0844761 8.7060205,4.99607289 8.81646861,4.99747011 Z M8.85,1.9 L5.15,1.9 L5.15,2.653 L8.85,2.653 L8.85,1.9 Z" fill-rule="nonzero"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -10,7 +10,8 @@ import DisplayInlineBlock from './display-inline-block.svg';
import DisplayFlex from './display-flex.svg';
import Harmmer from './hammer.svg';
import Dialog from './dialog.svg';
import API from './api.svg';
import Setting from './setting.svg';
import PickerIcon from './picker-icon.svg';
registerIcon('arrow-to-right', ArrowToRight);
registerIcon('left-arrow-to-left', LeftArrowToleft);
@ -19,7 +20,8 @@ registerIcon('arrow-to-bottom', ArrowToBottom);
registerIcon('collapse-open', CollapseOpen);
registerIcon('harmmer', Harmmer);
registerIcon('dialog', Dialog);
registerIcon('api', API);
registerIcon('setting', Setting);
registerIcon('picker-icon', PickerIcon);
// 「页面设计器改版」设计侧提供的icon组件头部工具栏icon
import CopyBtn from './copy-btn.svg';

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.5 14.5h-13v-13h13v1.998"></path>
<g stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path d="M12.775 4.943l1.768 1.768-1.766 1.765M13.85 6.71H8M9.694 9.235l-1.768 1.768 1.766 1.766M8.618 11.002l5.882-.001"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 471 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="1实体和表单选择-接口选择1" transform="translate(-834.000000, -334.000000)">
<g id="设置" transform="translate(834.000000, 334.000000)">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<path d="M11.4991,1.9996 L14.9991,8.0626 L11.4991,14.1246 L4.5001,14.1246 L1.0001,8.0626 L4.5001,1.9996 L11.4991,1.9996 Z M10.9221,3.0006 L5.0771,3.0006 L2.1551,8.0626 L5.0771,13.1236 L10.9221,13.1236 L13.8461,8.0626 L10.9221,3.0006 Z M7.9998,6.0625 C9.1048,6.0625 9.9998,6.9585 9.9998,8.0625 C9.9998,9.1665 9.1048,10.0625 7.9998,10.0625 C6.8958,10.0625 5.9998,9.1665 5.9998,8.0625 C5.9998,6.9585 6.8958,6.0625 7.9998,6.0625 Z M7.9998,7.0625 C7.4488,7.0625 6.9998,7.5105 6.9998,8.0625 C6.9998,8.6135 7.4488,9.0625 7.9998,9.0625 C8.5518,9.0625 8.9998,8.6135 8.9998,8.0625 C8.9998,7.5105 8.5518,7.0625 7.9998,7.0625 Z" id="图标-填色" fill="#84868C" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -22,6 +22,8 @@ export * from './manager';
export * from './plugin';
export * from './icons/index';
export * from './mocker';
export * from './builder/DSBuilder';
import './builder/ApiBuilder';
import {BasicEditor, RendererEditor} from './compat';
import MiniEditor from './component/MiniEditor';
import CodeEditor from './component/Panel/AMisCodeEditor';

View File

@ -189,5 +189,51 @@ extendLocale('en-US', {
'eadc8c8d4a8776893672330598babca0':
'Location error, target location not found',
'f3c057f37fb9a4e7dd44b04919c12578':
'Please click add new element from the component panel on the left.'
'Please click add new element from the component panel on the left.',
'54ea89b497ec3bb319c68844dfa3687f': '',
'51e213e66b37d716a35baebc9193035c': '',
'388e0ff896ea4f23e71d36c06443f157': '',
'b4bc91701b86fe8543d649e97daea602': '',
'f8747759c73697367bc8a570977c4a62': '',
'd2b46e7989e18239d7036affd4d521db': '',
'9caecd931b956381e0763d05aa42835c': '',
'47cd88592f6ef2b258f02c0690d267ed': '',
'32c65d8d7431e76029678ec7bb73a5ab': '',
'020586d0c69f8211840ddf9ee9bbf6ab': '',
'226b0912184333c81babf2f1894ec0c1': '',
'97d07614380da93d257f9fbf81aa56fb': '',
'20def7942674282277c3714ed7ea6ce0': '',
'4ff1e74e43a3586339251494117185ad': '',
'c7bff79d059a0b7ff9b02441959d8be2': '',
'3fea7ca76cdece641436d7ab0d02ab1b': '',
'9da188491dd34c4382a5b9f006194e41': '',
'b3e55578af5dd473bab62641bb2f5f8e': '',
'9b6425cd2d496c9cb5a6c6b8ff125d1b': '',
'15d169d28cd48c97fe751e4cc92ca926': '',
'9597dcaf432ceba92a160d61cb1ef65f': '',
'9913107b19cb6012250134ff91377430': '',
'454e60f5759903d7d3dba58e3f9bd590': '',
'db98f889ce6bc235e66bd4b2a788d137': '',
'006ded9fa277cf030592021f595a07d5': '',
'a6beb974cc0b50eebd18120b8110a88b': '',
'b339aa87104709397ba68e7ebbc6e5ba': '',
'481e034e6026969aae4ce7ce7c8a7b6f': '',
'6bfb9bb2218ff32b6139e98bc93707c0': '',
'24b6d4c0892a8f3ee2a982e3ab0afe38': '',
'4484fa04e7b71db4c8293e5bcb53eca4': '',
'4cc6a76c146c0360a41ceaf5e212c891': '',
'a9fea442707e26dee478b34a2f2ce263': '',
'91aa2166ee4811414381c8d94e6567e6': '',
'3712972d84adf48acbd6ad24b4d75ad0': '',
'66ab5e9f24c8f46012a25c89919fb191': '',
'e73cefac9d030927da1618c7b15c98c9': '',
'7fb62b30119c3797a843a48368463314': '',
'8d9a071ee2ef45e045968e117a205c07': '',
'55405ea6ff6fd823ffab7e6b10ddfa95': '',
'c26996a6506adf397f0668d376d0b40b': '',
'6ff4bf3d567e977aa4c90c27dff1e6db': '',
'9c4666fd08c2738eb9611a3721cb5f0f': '',
'a094e5b7699ea4b61094cc4120170423': '',
'eeb6908870e058bc23d52c1e405a054e': '',
'38ce27d84639f3a6e07c00b3b4995c0e': ''
});

View File

@ -1,6 +1,57 @@
import {extendLocale} from 'i18n-runtime';
extendLocale('zh-CN', {
'54ea89b497ec3bb319c68844dfa3687f': '接口',
'51e213e66b37d716a35baebc9193035c':
"用来保存数据, 表单提交后将数据传入此接口。 <br/>\n 接口响应体要求(如果data中有数据该数据将被合并到表单上下文中)<br/>\n {{@1}}\n}, null, '<br/>')}",
'388e0ff896ea4f23e71d36c06443f157':
"接口响应体要求:<br/>\n {{@1}},\n page: 0,\n total: 0\n}, null, '<br/>')}",
'b4bc91701b86fe8543d649e97daea602': '初始化接口',
'f8747759c73697367bc8a570977c4a62':
"接口响应体要求:<br/>\n {{@1}}\n}, null, '<br/>')}",
'd2b46e7989e18239d7036affd4d521db': '{{@1}}字段',
'9caecd931b956381e0763d05aa42835c': '字段',
'47cd88592f6ef2b258f02c0690d267ed': '隐藏,默认选中',
'32c65d8d7431e76029678ec7bb73a5ab': '标题',
'020586d0c69f8211840ddf9ee9bbf6ab': '绑定字段',
'226b0912184333c81babf2f1894ec0c1': '类型',
'97d07614380da93d257f9fbf81aa56fb': '文本',
'20def7942674282277c3714ed7ea6ce0': '图片',
'4ff1e74e43a3586339251494117185ad': '日期',
'c7bff79d059a0b7ff9b02441959d8be2': '进度',
'3fea7ca76cdece641436d7ab0d02ab1b': '状态',
'9da188491dd34c4382a5b9f006194e41': '映射',
'b3e55578af5dd473bab62641bb2f5f8e': '输入类型',
'9b6425cd2d496c9cb5a6c6b8ff125d1b': '输入框',
'15d169d28cd48c97fe751e4cc92ca926': '多行文本',
'9597dcaf432ceba92a160d61cb1ef65f': '数字输入',
'9913107b19cb6012250134ff91377430': '单选框',
'454e60f5759903d7d3dba58e3f9bd590': '勾选框',
'db98f889ce6bc235e66bd4b2a788d137': '复选框',
'006ded9fa277cf030592021f595a07d5': '下拉框',
'a6beb974cc0b50eebd18120b8110a88b': '开关',
'b339aa87104709397ba68e7ebbc6e5ba': '表格',
'481e034e6026969aae4ce7ce7c8a7b6f': '文件上传',
'6bfb9bb2218ff32b6139e98bc93707c0': '图片上传',
'24b6d4c0892a8f3ee2a982e3ab0afe38': '富文本编辑器',
'4484fa04e7b71db4c8293e5bcb53eca4': '添加字段',
'4cc6a76c146c0360a41ceaf5e212c891': '基于接口自动生成字段',
'a9fea442707e26dee478b34a2f2ce263': '请先填写接口',
'91aa2166ee4811414381c8d94e6567e6':
'API返回格式不正确请查看接口响应格式要求',
'3712972d84adf48acbd6ad24b4d75ad0': '列表',
'66ab5e9f24c8f46012a25c89919fb191': '新增',
'f26225bde6a250894a04db4c53ea03d0': '详情',
'95b351c86267f3aedf89520959bce689': '编辑',
'2f4aaddde33c9b93c36fd2503f3d122b': '删除',
'e73cefac9d030927da1618c7b15c98c9': '批量编辑',
'7fb62b30119c3797a843a48368463314': '批量删除',
'8d9a071ee2ef45e045968e117a205c07': '导入',
'55405ea6ff6fd823ffab7e6b10ddfa95': '导出',
'c26996a6506adf397f0668d376d0b40b': '简单查询',
'6ff4bf3d567e977aa4c90c27dff1e6db': '模糊查询',
'9c4666fd08c2738eb9611a3721cb5f0f': '高级查询',
'a094e5b7699ea4b61094cc4120170423': '数据来源',
'4e7f76261f8c4c6d78998f85fc1f4c6e': '外边距',
'16a20243f9b741c08216dc9548de2968': '整体',
'23ecf42cada8bf2715792d718544d107': '极小',
@ -74,7 +125,6 @@ extendLocale('zh-CN', {
'41150516bf0d90646edc5239593366e9': '选中组件插入到',
'd87481b371771b4f150da76e311bbbef': '输入关键字可过滤组件',
'becdc848350872592201e31bab03892a': '无法预览',
'f26225bde6a250894a04db4c53ea03d0': '详情',
'751dfe6f476903c21381c9acf88332e2': '没有可用组件,也许你该切换容器试试。',
'e22c9a05b424b761efce11f17726fdd7': '替换',
'9bdb07e72d3a9a6084201a7398523f5a': '插入',
@ -96,6 +146,8 @@ extendLocale('zh-CN', {
'0bd36c8db19e3a93506f39ebc8ff0ab9':
'当表单、列表等组件有名字时会出现在这里方便选择',
'bb28ec819520ced0ffb4c3da01f112e2': '点击清空当前区域',
'eeb6908870e058bc23d52c1e405a054e': '上一步',
'38ce27d84639f3a6e07c00b3b4995c0e': '下一步',
'e83a256e4f5bb4ff8b3d804b5473217a': '确认',
'22c799040acdb2601b437ed5449de076': '容器',
'bd9fcf46b4e5993f97fe04ee9ebcd7ed': '撤销',
@ -119,7 +171,6 @@ extendLocale('zh-CN', {
'1f81fd4598e9151538f29c41b8aa0020': '保存当前所有操作',
'645dbc5504e722a30896486085a06b32': '预览',
'5bc425ac8b75c571093a63eb6073c354': '开启预览模式',
'2f4aaddde33c9b93c36fd2503f3d122b': '删除',
'426cd14ebd62a4922186527d07ba37f3': '删除当前节点',
'499e58e764420aeed2d1476a56d8fa34': '向上移动',
'd040485f0e3887f0b297f8f772db03e4': '向上移动当前节点',
@ -150,7 +201,6 @@ extendLocale('zh-CN', {
'7f2f0461a58c43667d7245ce92bb2e77': '按住拖动调整位置',
'78c1c38b91c672da1113fa2564c14ea6': '向前插入组件',
'87f48bbadfbef5ef4554e06b7e141d37': '向后插入组件',
'95b351c86267f3aedf89520959bce689': '编辑',
'0ec9eaf9c3525eb110db58aae5912210': '更多',
'417db09508befe7dbe9f84a517a6edec': '重复一份',
'99b81127ef28368151621cdfccce69f8': '取消多选',

View File

@ -45,7 +45,9 @@ import {
reGenerateID,
isString,
isObject,
JSONPipeOut
JSONPipeOut,
generateNodeId,
JSONTraverse
} from './util';
import {reaction} from 'mobx';
import {hackIn, makeSchemaFormRender, makeWrapper} from './component/factory';
@ -58,8 +60,9 @@ import {EditorProps} from './component/Editor';
import findIndex from 'lodash/findIndex';
import {EditorDNDManager} from './dnd';
import {IScopedContext} from 'amis';
import {SchemaObject} from 'amis/lib/Schema';
import {SchemaObject, SchemaCollection} from 'amis/lib/Schema';
import type {RendererConfig} from 'amis-core/lib/factory';
import {isPlainObject} from 'lodash';
export interface EditorManagerConfig
extends Omit<EditorProps, 'value' | 'onChange'> {}
@ -439,6 +442,7 @@ export class EditorManager {
const node = this.store.getNodeById(id);
panels = node ? this.collectPanels(node, true) : panels;
}
this.store.setPanels(
panels.map(item => ({
...item,
@ -1277,9 +1281,12 @@ export class EditorManager {
let index: number = -1;
const commonContext = this.buildEventContext(id);
if (!('id' in json)) {
json = {...json, id: 'u:' + guid()};
// 填充id有些脚手架生成了复杂的布局等这里都填充一下id
JSONTraverse(json, (value: any) => {
if (isPlainObject(value) && value.type && !value.id) {
value.id = generateNodeId();
}
});
if (beforeId) {
const arr = commonContext.schema[region];
@ -1559,7 +1566,7 @@ export class EditorManager {
* @param schema
*/
makeSchemaFormRender(schema: {
body?: Array<any>;
body?: SchemaCollection;
controls?: Array<any>;
definitions?: any;
api?: any;
@ -1672,6 +1679,8 @@ export class EditorManager {
let scope: DataScope | void;
let from = node;
let region = node;
// 查找最近一层的数据域
while (!scope && from) {
const nodeId = from.info?.id;
const type = from.info?.type;
@ -1684,11 +1693,18 @@ export class EditorManager {
}
}
const nearestScope = scope;
let nearestScope;
// 更新组件树中的所有上下文数据声明为最新数据
while (scope) {
const [id, type] = scope.id.split('-');
const node = this.store.getNodeById(id, type);
// 拿非重复组件id的父组件作为主数据域展示如CRUD不展示表格只展示增删改查信息避免变量面板出现两份数据
if (!nearestScope && node && !node.isSecondFactor) {
nearestScope = scope;
}
const jsonschema = await node?.info?.plugin?.buildDataSchemas?.(
node,
region
@ -1710,6 +1726,44 @@ export class EditorManager {
: this.dataSchema.getSchemas();
}
/**
*
*/
async getAvailableContextFields(node: EditorNodeType) {
if (!node) {
return;
}
let scope: DataScope | void;
let from = node;
let region = node;
// 查找最近一层的数据域
while (!scope && from) {
scope = this.dataSchema.hasScope(`${from.id}-${from.type}`)
? this.dataSchema.getScope(`${from.id}-${from.type}`)
: undefined;
from = from.parent;
if (from?.isRegion) {
region = from;
}
}
while (scope) {
const [id, type] = scope.id.split('-');
const scopeNode = this.store.getNodeById(id, type);
if (scopeNode) {
return scopeNode?.info.plugin.getAvailableContextFields?.(
scopeNode,
node
);
}
scope = scope.parent;
}
}
beforeDispatchEvent(
originHook: any,
e: any,
@ -1725,7 +1779,11 @@ export class EditorManager {
component.props.$$id,
component.props.type
);
node?.info?.plugin?.rendererBeforeDispatchEvent?.(node, e, JSONPipeOut(data));
node?.info?.plugin?.rendererBeforeDispatchEvent?.(
node,
e,
JSONPipeOut(data)
);
}
}

View File

@ -1,7 +1,6 @@
/**
* @file interface BasePlugin
*/
import {RegionWrapperProps} from './component/RegionWrapper';
import {EditorManager} from './manager';
import {EditorStoreType} from './store/editor';
@ -13,7 +12,8 @@ import {DiffChange} from './util';
import find from 'lodash/find';
import type {RendererConfig} from 'amis-core/lib/factory';
import type {MenuDivider, MenuItem} from 'amis-ui/lib/components/ContextMenu';
import type {BaseSchema} from 'amis/lib/Schema';
import type {BaseSchema, SchemaCollection} from 'amis/lib/Schema';
import {DSFieldGroup} from './builder/DSBuilder';
/**
*
@ -243,7 +243,7 @@ export interface RendererInfo extends RendererScaffoldInfo {
wrapperProps?: any;
/**
* $$id
* $$id
* Combo
*/
filterProps?: (props: any, node: EditorNodeType) => any;
@ -311,12 +311,22 @@ export interface PopOverForm {
}
export interface ScaffoldForm extends PopOverForm {
mode?: 'normal' | 'horizontal' | 'inline';
// 内容是否是分步骤的如果是body必须是?: Array<{title: string,body: any[]}>
stepsBody?: boolean;
mode?:
| 'normal'
| 'horizontal'
| 'inline'
| {
mode: string;
horizontal: any;
};
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
className?: string;
initApi?: any;
api?: any;
actions?: any[];
/**
*
* key
@ -555,6 +565,12 @@ export interface ResizeMoveEventContext extends EventContext {
node: EditorNodeType;
}
export interface AfterBuildPanelBody extends EventContext {
data: SchemaCollection;
plugin: BasePlugin;
context: BaseEventContext;
}
/**
*
*/
@ -720,6 +736,9 @@ export interface PluginInterface
order?: number;
// 是否可绑定数据,一般容器类型就没有
withDataSource?: boolean;
/**
* getRendererInfo
*/
@ -768,11 +787,31 @@ export interface PluginInterface
*/
panelJustify?: boolean;
/**
*
*/
getAvailableContextFields?: (
// 提供数据域的容器节点
scopeNode: EditorNodeType,
// 数据域的应用节点
target: EditorNodeType,
// 节点所属的容器region
region?: EditorNodeType
) => Promise<SchemaCollection | void>;
/**
* @deprecated panelBodyCreator
*/
panelControlsCreator?: (context: BaseEventContext) => Array<any>;
panelBodyCreator?: (context: BaseEventContext) => Array<any>;
panelBodyCreator?: (context: BaseEventContext) => SchemaCollection;
/**
* panel还需要合并目标插件提供的配置plugin为准
*/
panelBodyMergeable?: (
context: BaseEventContext,
plugin: PluginInterface
) => boolean;
popOverBody?: Array<any>;
popOverBodyCreator?: (context: BaseEventContext) => Array<any>;
@ -879,7 +918,11 @@ export interface RendererPluginAction {
}
// 分支动作
export interface SubRendererPluginAction extends Pick<RendererPluginAction, 'actionType' | 'innerArgs' | 'descDetail'>{}
export interface SubRendererPluginAction
extends Pick<
RendererPluginAction,
'actionType' | 'innerArgs' | 'descDetail'
> {}
export interface PluginEvents {
[propName: string]: RendererPluginEvent[];
@ -957,6 +1000,16 @@ export abstract class BasePlugin implements PluginInterface {
plugin.panelBodyCreator) &&
context.info.plugin === this
) {
const body = plugin.panelBodyCreator
? plugin.panelBodyCreator(context)
: plugin.panelBody!;
this.manager.trigger('after-build-panel-body', {
context,
data: body,
plugin
});
panels.push({
key: 'config',
icon: plugin.panelIcon || plugin.icon || 'fa fa-cog',
@ -966,9 +1019,7 @@ export abstract class BasePlugin implements PluginInterface {
definitions: plugin.panelDefinitions,
submitOnChange: plugin.panelSubmitOnChange,
api: plugin.panelApi,
body: plugin.panelBodyCreator
? plugin.panelBodyCreator(context)
: plugin.panelBody!,
body: body,
controls: plugin.panelControlsCreator
? plugin.panelControlsCreator(context)
: plugin.panelControls!,

View File

@ -548,9 +548,9 @@ export class BasicToolbarPlugin extends BasePlugin {
menus: menus,
render: this.manager.makeSchemaFormRender({
body: [
// @ts-ignore amis中有问题可选参数搞成了必选改完了可以去掉这行
{
type: 'button-group',
block: true,
buttons: menus
.filter(item => item !== '|')
.map(menu => ({

View File

@ -538,7 +538,7 @@ export const EditorStore = types
);
}
return bcn;
return bcn.filter(item => !item.isSecondFactor);
},
get activePath(): Array<EditorNodeType> {

View File

@ -523,6 +523,18 @@ export const EditorNode = types
self.h = height;
}
function getClosestParentByType(type: string): EditorNodeType | void {
let node = self;
while(node = node.parent) {
if (node.schema.type === type) {
return node as EditorNodeType;
}
if (node.id === 'root') {
return;
}
}
}
// 放到props会变成 frozen 的。
let component: any;
@ -531,6 +543,7 @@ export const EditorNode = types
}
return {
getClosestParentByType,
updateIsCommonConfig,
addChild(props: {
id: string;

View File

@ -1,3 +1,5 @@
import { SchemaObject } from "amis/lib/Schema";
/**
* @file amis schema
*
@ -66,3 +68,25 @@ export function defaultValue(defaultValue: any, strictMode: boolean = true) {
? (value: any) => (typeof value === 'undefined' ? defaultValue : value)
: (value: any) => value || defaultValue;
}
/**
* label
*/
export function tipedLabel(
body: string | Array<SchemaObject>,
tip: string,
style?: React.CSSProperties
) {
return {
type: 'tooltip-wrapper',
tooltip: tip,
tooltipTheme: 'dark',
placement: 'top',
tooltipStyle: {
fontSize: '12px',
...(style || {})
},
className: 'ae-formItemControl-label-tip',
body
};
}

View File

@ -8,6 +8,7 @@ import DeepDiff, {Diff} from 'deep-diff';
import isPlainObject from 'lodash/isPlainObject';
import isNumber from 'lodash/isNumber';
import type {Schema} from 'amis/lib/types';
import {SchemaObject} from 'amis/lib/Schema';
const {
guid,
@ -513,10 +514,15 @@ export function JSONMoveDownById(json: any, id: string) {
});
}
export function JSONDuplicate(json: any, id: string) {
export function JSONDuplicate(
json: any,
id: string,
// 有时候复制时因为局部会有事件动作等内容需要改为复制部分的新id这里把老id与新id的关系存下来
reIds: {[propKey: string]: string} = {}
) {
return JSONChangeInArray(json, id, (arr: any[], node: any, index: number) => {
const copy = JSONPipeIn(JSONPipeOut(node));
arr.splice(index + 1, 0, reGenerateID(copy));
arr.splice(index + 1, 0, reGenerateID(copy, reIds));
});
}
@ -524,16 +530,24 @@ export function JSONDuplicate(json: any, id: string) {
*
* @param json
*/
export function reGenerateID(json: any) {
JSONTraverse(json, (value: any, key: string, host: any) => {
if (
key === 'id' &&
typeof value === 'string' &&
value.indexOf('u:') === 0 &&
host
export function reGenerateID(
json: any,
// 有时候复制时因为局部会有事件动作等内容需要改为复制部分的新id这里把老id与新id的关系存下来
reIds: {[propKey: string]: string} = {}
) {
host.id = 'u:' + guid();
JSONTraverse(json, (value: any, key: string, host: any) => {
const isNodeIdFormat =
typeof value === 'string' && value.indexOf('u:') === 0;
if (key === 'id' && isNodeIdFormat && host) {
const newID = generateNodeId();
reIds[host.id] = newID;
host.id = newID;
}
// 组件ID给新的id内容
else if (key === 'componentId' && isNodeIdFormat) {
host.componentId = reIds[value] ?? value;
}
return value;
});
return json;
@ -873,6 +887,13 @@ export function jsonToJsonSchema(json: any = {}) {
return jsonschema;
}
/**
* id
*/
export function generateNodeId() {
return 'u:' + guid();
}
// 是否使用 plugin 自带的 svg 版 icon
export function isHasPluginIcon(plugin: any) {
return plugin.pluginIcon && hasIcon(plugin.pluginIcon);

View File

@ -35,10 +35,12 @@ module.exports = {
}
}
},
/*
{
loader: 'webpack-react-i18n',
options: i18nConfig
}
*/
]
},