diff --git a/packages/amis-editor/package.json b/packages/amis-editor/package.json index e9f110974..0ad9f0d3c 100644 --- a/packages/amis-editor/package.json +++ b/packages/amis-editor/package.json @@ -1,6 +1,6 @@ { "name": "amis-editor", - "version": "5.2.1", + "version": "5.2.1-beta.2", "description": "amis 可视化编辑器", "main": "lib/index.js", "module": "esm/index.js", @@ -61,24 +61,14 @@ "@types/sortablejs": "^1.10.7", "@types/tinycolor2": "^1.4.3", "ajv": "^8.8.2", - "amis": "2.3.2-beta.1", - "amis-core": "2.3.2-beta.1", - "amis-formula": "2.3.2-beta.1", - "amis-ui": "2.3.2-beta.1", + "amis": "2.4.0", + "amis-core": "2.4.0", + "amis-formula": "2.4.0", + "amis-ui": "2.4.0", "axios": "0.21.1", "concurrently": "^6.2.0", "css-loader": "^6.2.0", "faker": "^5.5.3", - "fis-parser-sass": "^1.1.0", - "fis-parser-svgr": "^1.0.1", - "fis3": "^3.5.0-beta.0", - "fis3-hook-commonjs": "^0.1.32", - "fis3-hook-node_modules": "^2.3.1", - "fis3-parser-typescript": "^1.4.0", - "fis3-postpackager-loader": "^2.1.12", - "fis3-prepackager-stand-alone-pack": "^1.0.1", - "fis3-preprocessor-js-require-css": "^0.1.3", - "fis3-preprocessor-js-require-file": "^0.1.3", "husky": "^7.0.0", "lint-staged": "^12.1.2", "mini-css-extract-plugin": "^2.3.0", diff --git a/packages/amis-editor/src/plugin/Chart.tsx b/packages/amis-editor/src/plugin/Chart.tsx index 1fd1624e9..810a75e9f 100644 --- a/packages/amis-editor/src/plugin/Chart.tsx +++ b/packages/amis-editor/src/plugin/Chart.tsx @@ -8,8 +8,10 @@ import { diff, defaultValue, getSchemaTpl, - CodeEditor as AmisCodeEditor + CodeEditor as AmisCodeEditor, + RendererPluginEvent } from 'amis-editor-core'; +import {getEventControlConfig} from '../renderer/event-control/helper'; const ChartConfigEditor = ({value, onChange}: any) => { return ( @@ -19,6 +21,54 @@ const ChartConfigEditor = ({value, onChange}: any) => { ); }; +const DEFAULT_EVENT_PARAMS = [ + { + type: 'object', + properties: { + 'event.data.componentType': { + type: 'string', + title: 'componentType' + }, + 'event.data.seriesType': { + type: 'string', + title: 'seriesType' + }, + 'event.data.seriesIndex': { + type: 'number', + title: 'seriesIndex' + }, + 'event.data.seriesName': { + type: 'string', + title: 'seriesName' + }, + 'event.data.name': { + type: 'string', + title: 'name' + }, + 'event.data.dataIndex': { + type: 'number', + title: 'dataIndex' + }, + 'event.data.data': { + type: 'object', + title: 'data' + }, + 'event.data.dataType': { + type: 'string', + title: 'dataType' + }, + 'event.data.value': { + type: 'number', + title: 'data' + }, + 'event.data.color': { + type: 'string', + title: 'color' + } + } + } +]; + export class ChartPlugin extends BasePlugin { // 关联渲染器名字 rendererName = 'chart'; @@ -56,6 +106,42 @@ export class ChartPlugin extends BasePlugin { ...this.scaffold }; + // 事件定义 + events: RendererPluginEvent[] = [ + { + eventName: 'click', + eventLabel: '鼠标点击', + description: '鼠标点击时触发', + dataSchema: DEFAULT_EVENT_PARAMS + }, + { + eventName: 'mouseover', + eventLabel: '鼠标悬停', + description: '鼠标悬停时触发', + dataSchema: DEFAULT_EVENT_PARAMS + }, + { + eventName: 'legendselectchanged', + eventLabel: '切换图例选中状态', + description: '切换图例选中状态时触发', + dataSchema: [ + { + type: 'object', + properties: { + 'event.data.name': { + type: 'string', + title: 'name' + }, + 'event.data.selected': { + type: 'object', + title: 'selected' + } + } + } + ] + } + ]; + // 动作定义 actions: RendererPluginAction[] = [ { @@ -68,6 +154,7 @@ export class ChartPlugin extends BasePlugin { actionLabel: '更新数据', description: '触发组件数据更新' } + // 特性动作太多了,这里先不加了,可以通过写代码配置 ]; panelTitle = '图表'; @@ -184,6 +271,16 @@ export class ChartPlugin extends BasePlugin { { title: '其他', body: [getSchemaTpl('name')] + }, + { + title: '事件', + className: 'p-none', + body: [ + getSchemaTpl('eventControl', { + name: 'onEvent', + ...getEventControlConfig(this.manager, context) + }) + ] } ]) ]; diff --git a/packages/amis-editor/src/plugin/Form/InputDateRange.tsx b/packages/amis-editor/src/plugin/Form/InputDateRange.tsx index d2ab4e4b7..736c29eca 100644 --- a/packages/amis-editor/src/plugin/Form/InputDateRange.tsx +++ b/packages/amis-editor/src/plugin/Form/InputDateRange.tsx @@ -5,15 +5,19 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core'; import {ValidatorTag} from '../../validator'; import {getEventControlConfig} from '../../renderer/event-control/helper'; import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core'; +import {getRendererByName} from 'amis-core'; const DateType: { [key: string]: { format: string; placeholder: string; ranges: string[]; + sizeMutable?: boolean; + type?: string; }; } = { date: { + ...getRendererByName('input-date-range'), format: 'YYYY-MM-DD', placeholder: '请选择日期范围', ranges: [ @@ -26,6 +30,7 @@ const DateType: { ] }, datetime: { + ...getRendererByName('input-datetime-range'), format: 'YYYY-MM-DD HH:mm:ss', placeholder: '请选择日期时间范围', ranges: [ @@ -38,27 +43,35 @@ const DateType: { ] }, time: { + ...getRendererByName('input-time-range'), format: 'HH:mm', placeholder: '请选择时间范围', ranges: [] }, month: { + ...getRendererByName('input-month-range'), format: 'YYYY-MM', placeholder: '请选择月份范围', ranges: [] }, quarter: { + ...getRendererByName('input-quarter-range'), format: 'YYYY [Q]Q', placeholder: '请选择季度范围', ranges: ['thisquarter', 'prevquarter'] }, year: { + ...getRendererByName('input-year-range'), format: 'YYYY', placeholder: '请选择年范围', ranges: ['thisyear', 'lastYear'] } }; +const sizeImmutableComponents = Object.values(DateType) + .map(item => (item?.sizeMutable === false ? item.type : null)) + .filter(a => a); + const tipedLabelText = '支持 now、+1day、-2weeks、+1hours、+2years这种相对值用法,同时支持变量如\\${start_date}'; @@ -206,7 +219,11 @@ export class DateRangeControlPlugin extends BasePlugin { minDate: '', maxDate: '', value: '', - ranges: DateType[type]?.ranges + ranges: DateType[type]?.ranges, + // size immutable 组件去除 size 字段 + size: sizeImmutableComponents.includes(value) + ? undefined + : form.data?.size }); } }), @@ -307,16 +324,27 @@ export class DateRangeControlPlugin extends BasePlugin { }), getSchemaTpl('dateShortCutControl', { mode: 'normal', - dropDownOption: { - 'yesterday': '昨天', - 'thisweek': '这个周', - 'prevweek': '上周', - '7daysago': '最近7天', - 'thismonth': '这个月', - 'prevmonth': '上个月', - 'thisquarter': '这个季度', - 'prevquarter': '上个季度', - 'thisyear': '今年' + normalDropDownOption: { + yesterday: '昨天', + thisweek: '这个周', + prevweek: '上周', + thismonth: '这个月', + prevmonth: '上个月', + thisquarter: '这个季度', + prevquarter: '上个季度', + thisyear: '今年' + }, + customDropDownOption: { + daysago: '最近n天', + dayslater: 'n天以内', + weeksago: '最近n周', + weekslater: 'n周以内', + monthsago: '最近n月', + monthslater: 'n月以内', + quartersago: '最近n季度', + quarterslater: 'n季度以内', + yearsago: '最近n年', + yearslater: 'n年以内' } }), // getSchemaTpl('remark'), @@ -349,7 +377,17 @@ export class DateRangeControlPlugin extends BasePlugin { body: getSchemaTpl( 'collapseGroup', [ - getSchemaTpl('style:formItem', renderer), + getSchemaTpl('style:formItem', { + renderer: {...renderer, sizeMutable: false}, + schema: [ + // 需要作为一个字符串表达式传入,因为切换 type 后 panelBodyCreator 不会重新执行 + getSchemaTpl('formItemSize', { + hiddenOn: `["${sizeImmutableComponents.join( + '","' + )}"].includes(this.type)` + }) + ] + }), getSchemaTpl('style:classNames', [ getSchemaTpl('className', { label: '描述', diff --git a/packages/amis-editor/src/plugin/Form/InputNumber.tsx b/packages/amis-editor/src/plugin/Form/InputNumber.tsx index f5c0a7241..0c40fb19a 100644 --- a/packages/amis-editor/src/plugin/Form/InputNumber.tsx +++ b/packages/amis-editor/src/plugin/Form/InputNumber.tsx @@ -32,7 +32,8 @@ export class NumberControlPlugin extends BasePlugin { scaffold = { type: 'input-number', label: '数字', - name: 'number' + name: 'number', + keyboard: true }; previewSchema: any = { type: 'form', @@ -137,7 +138,16 @@ export class NumberControlPlugin extends BasePlugin { required: true }), getSchemaTpl('label'), - getSchemaTpl('numberSwitchKeyboard'), + { + type: 'switch', + label: tipedLabel( + '键盘事件', + '通过键盘上下方向键来加减数据值' + ), + name: 'keyboard', + value: true, + inputClassName: 'is-inline' + }, getSchemaTpl('kilobitSeparator'), getSchemaTpl('valueFormula', { @@ -152,10 +162,7 @@ export class NumberControlPlugin extends BasePlugin { value: context?.schema.min }, needDeleteProps: ['min'], // 避免自我限制 - label: tipedLabel( - '最小值', - '请输入数字或使用 \\${xxx} 来获取变量,否则该配置不生效' - ), + label: '最小值', valueType: 'number' }), @@ -166,10 +173,7 @@ export class NumberControlPlugin extends BasePlugin { value: context?.schema.max }, needDeleteProps: ['max'], // 避免自我限制 - label: tipedLabel( - '最大值', - '请输入数字或使用 \\${xxx} 来获取变量,否则该配置不生效' - ), + label: '最大值', valueType: 'number' }), @@ -194,18 +198,12 @@ export class NumberControlPlugin extends BasePlugin { { type: 'input-text', name: 'prefix', - label: tipedLabel( - '前缀', - '仅在输入内容前展示,不包含在数据值中' - ) + label: tipedLabel('前缀', '输入内容前展示,不包含在数据值中') }, { type: 'input-text', name: 'suffix', - label: tipedLabel( - '后缀', - '仅在输入内容前展示,不包含在数据值中' - ) + label: tipedLabel('后缀', '输入内容后展示,不包含在数据值中') }, getSchemaTpl('combo-container', { @@ -263,7 +261,7 @@ export class NumberControlPlugin extends BasePlugin { label: '快捷编辑', name: 'displayMode', type: 'select', - value: 'base', + pipeIn: defaultValue('base'), options: [ { label: '单侧按钮', diff --git a/packages/amis-editor/src/plugin/Form/InputRating.tsx b/packages/amis-editor/src/plugin/Form/InputRating.tsx index ff8de4262..a4f187598 100644 --- a/packages/amis-editor/src/plugin/Form/InputRating.tsx +++ b/packages/amis-editor/src/plugin/Form/InputRating.tsx @@ -112,28 +112,18 @@ export class RateControlPlugin extends BasePlugin { valueType: 'number', // 期望数值类型 visibleOn: '!data.multiple' }), - + // 评分组件没有 min、max 属性,有 count 属性 getSchemaTpl('valueFormula', { - name: 'min', + name: 'count', rendererSchema: { ...context?.schema, - type: 'input-number' + type: 'input-number', + max: 10, + min: 1, + step: 1, + precision: 0 }, - needDeleteProps: ['min'], // 避免自我限制 - label: tipedLabel( - '最小值', - '请输入数字或使用 \\${xxx} 来获取变量,否则该配置不生效' - ), - valueType: 'number' - }), - - getSchemaTpl('valueFormula', { - name: 'max', - rendererSchema: { - ...context?.schema, - type: 'input-number' - }, - needDeleteProps: ['max'], // 避免自我限制 + needDeleteProps: ['count'], // 避免自我限制 label: tipedLabel( '最大值', '请输入数字或使用 \\${xxx} 来获取变量,否则该配置不生效' diff --git a/packages/amis-editor/src/plugin/Form/InputTable.tsx b/packages/amis-editor/src/plugin/Form/InputTable.tsx index e80c50b0c..5a19cc8bd 100644 --- a/packages/amis-editor/src/plugin/Form/InputTable.tsx +++ b/packages/amis-editor/src/plugin/Form/InputTable.tsx @@ -190,13 +190,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.addable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'addBtnIcon', label: '增加按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.addable' - }, + }), getSchemaTpl('api', { name: 'addApi', label: '新增时提交的 API', @@ -213,13 +212,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.removable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'deleteBtnIcon', label: '删除按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.removable' - }, + }), getSchemaTpl('api', { name: 'deleteApi', label: '删除时提交的 API', @@ -236,13 +234,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.editable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'editBtnIcon', label: '编辑按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.editable' - }, + }), getSchemaTpl('switch', { label: '是否可复制', name: 'copyable' @@ -254,13 +251,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.copyable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'copyBtnIcon', label: '复制按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.copyable' - }, + }), getSchemaTpl('api', { name: 'updateApi', label: '修改时提交的 API', @@ -273,13 +269,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.editable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'confirmBtnIcon', label: '确认编辑按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.editable' - }, + }), { type: 'input-text', name: 'cancelBtnLabel', @@ -287,13 +282,12 @@ export class TableControlPlugin extends BasePlugin { visibleOn: 'data.editable', pipeIn: defaultValue('') }, - { + getSchemaTpl('icon', { name: 'cancelBtnIcon', label: '取消编辑按钮图标', - type: 'icon-picker', className: 'fix-icon-picker-overflow', visibleOn: 'data.editable' - }, + }), getSchemaTpl('switch', { label: '是否可拖拽排序', name: 'draggable' diff --git a/packages/amis-editor/src/plugin/Link.tsx b/packages/amis-editor/src/plugin/Link.tsx index 70421f466..32cc00696 100644 --- a/packages/amis-editor/src/plugin/Link.tsx +++ b/packages/amis-editor/src/plugin/Link.tsx @@ -51,35 +51,16 @@ export class LinkPlugin extends BasePlugin { name: 'blank', label: '在新窗口打开' }), - { - label: '图标位置', - type: 'button-group-select', - name: 'position', - pipeIn: defaultValue('rightIcon'), - size: 'sm', - value: 'rightIcon', - options: [ - { - label: '左侧', - value: 'icon' - }, - - { - label: '右侧', - value: 'rightIcon' - } - ] - }, getSchemaTpl('iconLink', { name: 'icon', - visibleOn: 'this.position == "icon"' + label: '左侧图标' }), getSchemaTpl('iconLink', { name: 'rightIcon', - visibleOn: 'this.position == "rightIcon"' - }), + label: '右侧图标' + }) ] }, getSchemaTpl('status', { diff --git a/packages/amis-editor/src/plugin/Page.tsx b/packages/amis-editor/src/plugin/Page.tsx index e1333ae2b..59fa4bc09 100644 --- a/packages/amis-editor/src/plugin/Page.tsx +++ b/packages/amis-editor/src/plugin/Page.tsx @@ -210,6 +210,7 @@ export class PagePlugin extends BasePlugin { }), getSchemaTpl('apiControl', { name: 'initApi', + mode: 'row', labelClassName: 'none', label: tipedLabel( '初始化接口', diff --git a/packages/amis-editor/src/renderer/APIControl.tsx b/packages/amis-editor/src/renderer/APIControl.tsx index b8ecd0d99..ad4875271 100644 --- a/packages/amis-editor/src/renderer/APIControl.tsx +++ b/packages/amis-editor/src/renderer/APIControl.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import merge from 'lodash/merge'; -import isEqual from 'lodash/isEqual'; +import mergeWith from 'lodash/mergeWith'; import cloneDeep from 'lodash/cloneDeep'; import cx from 'classnames'; import {FormItem, Icon} from 'amis'; @@ -324,7 +323,20 @@ export default class APIControl extends React.Component< } if (typeof value !== 'string' || typeof values !== 'string') { - api = merge({}, normalizeApi(values)); + api = mergeWith({}, normalizeApi(value), normalizeApi(values), + (value, srcValue, key) => { + // 这三个支持删除单个key的属性需用新值完全替换 + // 否则删除无效 + if (['data', 'responseData', 'headers'].includes(key)) { + return srcValue; + } + } + ); + ['data', 'responseData', 'headers'].forEach((item: keyof(Api)) => { + if (api[item] == null) { + delete api[item]; + } + }); } onChange?.(api); } diff --git a/packages/amis-editor/src/renderer/ActionApiControl.tsx b/packages/amis-editor/src/renderer/ActionApiControl.tsx index 83c9a5dc9..4809cc188 100644 --- a/packages/amis-editor/src/renderer/ActionApiControl.tsx +++ b/packages/amis-editor/src/renderer/ActionApiControl.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import merge from 'lodash/merge'; +import mergeWith from 'lodash/mergeWith'; import cloneDeep from 'lodash/cloneDeep'; import cx from 'classnames'; import {FormItem, InputBox} from 'amis'; @@ -195,7 +195,20 @@ export default class APIControl extends React.Component< } if (typeof value !== 'string' || typeof values !== 'string') { - api = merge({}, normalizeApi(value), normalizeApi(values)); + api = mergeWith({}, normalizeApi(value), normalizeApi(values), + (value, srcValue, key) => { + // 这三个支持删除单个key的属性需用新值完全替换 + // 否则删除无效 + if (['data', 'responseData', 'headers'].includes(key)) { + return srcValue; + } + } + ); + ['data', 'responseData', 'headers'].forEach((item: keyof(Api)) => { + if (api[item] == null) { + delete api[item]; + } + }); } onChange?.(api); diff --git a/packages/amis-editor/src/renderer/DateShortCutControl.tsx b/packages/amis-editor/src/renderer/DateShortCutControl.tsx index ec6d3c8e1..81bd114e2 100644 --- a/packages/amis-editor/src/renderer/DateShortCutControl.tsx +++ b/packages/amis-editor/src/renderer/DateShortCutControl.tsx @@ -5,41 +5,71 @@ import React from 'react'; import cx from 'classnames'; import Sortable from 'sortablejs'; import {findDOMNode} from 'react-dom'; -import {FormItem, Button, Icon, InputBox} from 'amis'; +import {FormItem, Icon} from 'amis'; import type {FormControlProps} from 'amis-core'; import type {BaseEventContext} from 'amis-editor-core'; +import type {Option} from 'amis'; import {autobind} from 'amis-editor-core'; -import FormulaControl from './FormulaControl'; -const CustomType = 'custom'; - -type RangesType = Array; - -type DropDownOption = { +type $Object = { [key: string]: string; }; +enum RangeType { + Normal = 'Normal', + Custom = 'Custom' +} + export interface DateShortCutControlProps extends FormControlProps { className?: string; /** * 编辑器上下文数据,用于获取字段所在Form的其他字段 */ context: BaseEventContext; - dropDownOption: DropDownOption; + normalDropDownOption: $Object; + customDropDownOption: $Object; } interface OptionsType { - label: string; - type: string; - inputValue: string; + label?: string; + value: string; + type: RangeType; + inputType?: string; } interface DateShortCutControlState { options: Array; } +interface InputOption { + type: 'middle' | 'suffix', + prefix?: string, + suffix: string +} + +const ShortCutItemWrap = ( + props: { + index: number, + children: React.ReactNode, + handleDelete: (index: number, e: React.SyntheticEvent) => void + }) => { + return ( + <> + + + {props.children} + + props.handleDelete(props.index, e)}> + + + + ); +} + const klass = 'ae-DateShortCutControl'; export class DateShortCutControl extends React.PureComponent< @@ -49,10 +79,8 @@ export class DateShortCutControl extends React.PureComponent< sortable?: Sortable; drag?: HTMLElement | null; target: HTMLElement | null; - dropDownOptionArr: Array<{ - label: string; - value: string; - }>; + normalDropDownOptionArr: Array