mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
Pick: #9446、#9440、#9439 (#9459)
* chore: 优化编辑器选中顶部菜单栏三个点呼出的功能菜单位置,防止被挡住 (#9446) * chore: 优化编辑器选中顶部菜单栏三个点呼出的功能菜单位置,防止被挡住 * fix: 修复新出来的节点无法点选的问题 * chore: 还原相关逻辑 * fix: 修复 condition-builder 操作符只有一个时不可以点选的问题 (#9439) * fix: 修复 crud nested 模式深层次点选异常 (#9440)
This commit is contained in:
parent
e7bfbf77b3
commit
36ecaaa21b
@ -471,9 +471,9 @@ const data = [
|
||||
version: '1',
|
||||
grade: 'A'
|
||||
}
|
||||
].map(function (child, i) {
|
||||
].map(function (child, j) {
|
||||
return Object.assign({}, child, {
|
||||
id: (i + 1) * 100 + (index + 1) * 1000 + i + 1
|
||||
id: (index + 1) * 10000 + (i + 1) * 100 + 1 + j
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@ -1529,11 +1529,11 @@ export const TableStore = iRendererStore
|
||||
self.selectedRows.clear();
|
||||
|
||||
selected.forEach(item => {
|
||||
let resolved = self.rows.find(a => a.pristine === item);
|
||||
let resolved = findTree(self.rows, a => a.pristine === item);
|
||||
|
||||
// 先严格比较,
|
||||
if (!resolved) {
|
||||
resolved = self.rows.find(a => {
|
||||
resolved = findTree(self.rows, a => {
|
||||
const selectValue = item[valueField || 'value'];
|
||||
const itemValue = a.pristine[valueField || 'value'];
|
||||
return selectValue === itemValue;
|
||||
@ -1542,14 +1542,14 @@ export const TableStore = iRendererStore
|
||||
|
||||
// 再宽松比较
|
||||
if (!resolved) {
|
||||
resolved = self.rows.find(a => {
|
||||
resolved = findTree(self.rows, a => {
|
||||
const selectValue = item[valueField || 'value'];
|
||||
const itemValue = a.pristine[valueField || 'value'];
|
||||
return selectValue == itemValue;
|
||||
});
|
||||
}
|
||||
|
||||
resolved && self.selectedRows.push(resolved);
|
||||
resolved && self.selectedRows.push(resolved as any);
|
||||
});
|
||||
|
||||
updateCheckDisable();
|
||||
|
@ -253,7 +253,8 @@ export function isObjectShallowModified(
|
||||
next: any,
|
||||
strictModeOrFunc: boolean | ((lhs: any, rhs: any) => boolean) = true,
|
||||
ignoreUndefined: boolean = false,
|
||||
stack: Array<any> = []
|
||||
stack: Array<any> = [],
|
||||
maxDepth: number = -1
|
||||
): boolean {
|
||||
if (Array.isArray(prev) && Array.isArray(next)) {
|
||||
return prev.length !== next.length
|
||||
@ -310,6 +311,9 @@ export function isObjectShallowModified(
|
||||
}
|
||||
|
||||
stack.push(prev);
|
||||
if (maxDepth > 0 && stack.length > maxDepth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i: number = keys.length - 1; i >= 0; i--) {
|
||||
const key = keys[i];
|
||||
|
20
packages/amis-core/src/utils/labelToString.ts
Normal file
20
packages/amis-core/src/utils/labelToString.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
|
||||
export function labelToString(label: any): string {
|
||||
const type = typeof label;
|
||||
if (type === 'string') {
|
||||
return label;
|
||||
} else if (type === 'number') {
|
||||
return `${label}`;
|
||||
}
|
||||
|
||||
if (isPlainObject(label)) {
|
||||
for (let key of ['__title', 'label', Object.keys(label)[0]]) {
|
||||
if (typeof label[key] === 'string') {
|
||||
return label[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'invalid label';
|
||||
}
|
@ -1,5 +1,16 @@
|
||||
import {Evaluator, parse, evaluateForAsync} from 'amis-formula';
|
||||
|
||||
const AST_CACHE: {[key: string]: any} = {};
|
||||
export function memoParse(str: string, options?: any) {
|
||||
let key = `${str}${options?.evalMode ? '-eval' : ''}${
|
||||
options?.allowFilter ? '-filter' : ''
|
||||
}${options?.variableMode ? '-variable' : ''}`;
|
||||
|
||||
const ast = AST_CACHE[key] || parse(str, options);
|
||||
AST_CACHE[key] = ast;
|
||||
return ast;
|
||||
}
|
||||
|
||||
export const tokenize = (
|
||||
str: string,
|
||||
data: object,
|
||||
@ -10,10 +21,11 @@ export const tokenize = (
|
||||
}
|
||||
|
||||
try {
|
||||
const ast = parse(str, {
|
||||
const ast = memoParse(str, {
|
||||
evalMode: false,
|
||||
allowFilter: true
|
||||
});
|
||||
|
||||
const result = new Evaluator(data, {
|
||||
defaultFilter
|
||||
}).evalute(ast);
|
||||
|
@ -2,6 +2,7 @@ import {register as registerBulitin, getFilters} from './tpl-builtin';
|
||||
import {register as registerLodash} from './tpl-lodash';
|
||||
import {parse, evaluate} from 'amis-formula';
|
||||
import {resolveCondition} from './resolveCondition';
|
||||
import {memoParse} from './tokenize';
|
||||
|
||||
export interface Enginer {
|
||||
test: (tpl: string) => boolean;
|
||||
@ -145,14 +146,10 @@ export async function evalExpressionWithConditionBuilder(
|
||||
return evalExpression(String(expression), data);
|
||||
}
|
||||
|
||||
const AST_CACHE: {[key: string]: any} = {};
|
||||
function evalFormula(expression: string, data: any) {
|
||||
const ast =
|
||||
AST_CACHE[expression] ||
|
||||
parse(expression, {
|
||||
evalMode: false
|
||||
});
|
||||
AST_CACHE[expression] = ast;
|
||||
const ast = memoParse(expression, {
|
||||
evalMode: false
|
||||
});
|
||||
|
||||
return evaluate(ast, data, {
|
||||
defaultFilter: 'raw'
|
||||
|
@ -38,6 +38,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
|
||||
if ((node.draggable || draggableContainer) && !isSorptionContainer) {
|
||||
toolbars.push({
|
||||
id: 'drag',
|
||||
iconSvg: 'drag-btn',
|
||||
icon: 'fa fa-arrows',
|
||||
tooltip: '按住拖动调整位置',
|
||||
@ -127,6 +128,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
|
||||
toolbars.push(
|
||||
{
|
||||
id: 'insert-before',
|
||||
iconSvg: 'left-arrow-to-left',
|
||||
tooltip: '向前插入组件',
|
||||
// level: 'special',
|
||||
@ -146,6 +148,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'insert-after',
|
||||
iconSvg: 'arrow-to-right',
|
||||
tooltip: '向后插入组件',
|
||||
// level: 'special',
|
||||
@ -173,6 +176,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
(node.info.plugin.popOverBody || node.info.plugin.popOverBodyCreator)
|
||||
) {
|
||||
toolbars.push({
|
||||
id: 'edit',
|
||||
icon: 'fa fa-pencil',
|
||||
tooltip: '编辑',
|
||||
placement: 'bottom',
|
||||
@ -193,6 +197,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
|
||||
if (node.removable || node.removable === undefined) {
|
||||
toolbars.push({
|
||||
id: 'delete',
|
||||
iconSvg: 'delete-btn',
|
||||
icon: 'fa',
|
||||
tooltip: '删除',
|
||||
@ -203,6 +208,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
}
|
||||
|
||||
toolbars.push({
|
||||
id: 'more',
|
||||
iconSvg: 'more-btn',
|
||||
icon: 'fa fa-cog',
|
||||
tooltip: '更多',
|
||||
@ -213,8 +219,18 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
const info = (
|
||||
e.target as HTMLElement
|
||||
).parentElement!.getBoundingClientRect();
|
||||
|
||||
// 150 是 contextMenu 的宽度
|
||||
// 默认右对齐
|
||||
let x = window.scrollX + info.left + info.width - 150;
|
||||
|
||||
// 显示不全是改成左对齐
|
||||
if (x < 0) {
|
||||
x = window.scrollX + info.left;
|
||||
}
|
||||
|
||||
this.manager.openContextMenu(id, '', {
|
||||
x: window.scrollX + info.left + info.width - 155,
|
||||
x: x,
|
||||
y: window.scrollY + info.top + info.height + 8
|
||||
});
|
||||
}
|
||||
@ -223,6 +239,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
|
||||
if (info.scaffoldForm?.canRebuild ?? info.plugin.scaffoldForm?.canRebuild) {
|
||||
toolbars.push({
|
||||
id: 'build',
|
||||
iconSvg: 'harmmer',
|
||||
tooltip: `快速构建「${info.plugin.name}」`,
|
||||
placement: 'bottom',
|
||||
@ -247,6 +264,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
if (selections.length) {
|
||||
// 多选时的右键菜单
|
||||
menus.push({
|
||||
id: 'copy',
|
||||
label: '重复一份',
|
||||
icon: 'copy-icon',
|
||||
disabled: selections.some(item => !item.node.duplicatable),
|
||||
@ -254,12 +272,14 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'unselect',
|
||||
label: '取消多选',
|
||||
icon: 'cancel-icon',
|
||||
onSelect: () => store.setActiveId(id)
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'delete',
|
||||
label: '删除',
|
||||
icon: 'delete-icon',
|
||||
disabled: selections.some(item => !item.node.removable),
|
||||
@ -281,23 +301,27 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
});
|
||||
*/
|
||||
menus.push({
|
||||
id: 'insert',
|
||||
label: '插入组件',
|
||||
onHighlight: (isOn: boolean) => isOn && store.setHoverId(id, region),
|
||||
onSelect: () => store.showInsertRendererPanel()
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'clear',
|
||||
label: '清空',
|
||||
onSelect: () => manager.emptyRegion(id, region)
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'paste',
|
||||
label: '粘贴',
|
||||
onSelect: () => manager.paste(id, region)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
menus.push({
|
||||
id: 'select',
|
||||
label: `选中${first.label}`,
|
||||
disabled: store.activeId === first.id,
|
||||
data: id,
|
||||
@ -326,6 +350,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
}
|
||||
|
||||
menus.push({
|
||||
id: 'unselect',
|
||||
label: '取消选中',
|
||||
disabled: !store.activeId || store.activeId !== id,
|
||||
onSelect: () => store.setActiveId('')
|
||||
@ -334,23 +359,27 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
menus.push('|');
|
||||
|
||||
menus.push({
|
||||
id: 'copy',
|
||||
label: '重复一份',
|
||||
disabled: !node.duplicatable,
|
||||
onSelect: () => manager.duplicate(id)
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'copy-config',
|
||||
label: '复制配置',
|
||||
onSelect: () => manager.copy(id)
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'cat-config',
|
||||
label: '剪切配置',
|
||||
disabled: !node.removable,
|
||||
onSelect: () => manager.cut(id)
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'paste-config',
|
||||
label: '粘贴配置',
|
||||
disabled:
|
||||
!Array.isArray(parent) ||
|
||||
@ -361,6 +390,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'delete',
|
||||
label: '删除',
|
||||
disabled: !node.removable,
|
||||
className: 'text-danger',
|
||||
@ -372,6 +402,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
const idx = Array.isArray(parent) ? parent.indexOf(schema) : -1;
|
||||
|
||||
menus.push({
|
||||
id: 'move-forward',
|
||||
label: '向前移动',
|
||||
disabled: !(Array.isArray(parent) && idx > 0) || !node.moveable,
|
||||
// || !node.prevSibling,
|
||||
@ -379,6 +410,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'move-backward',
|
||||
label: '向后移动',
|
||||
disabled:
|
||||
!(Array.isArray(parent) && idx < parent.length - 1) || !node.moveable,
|
||||
@ -450,12 +482,14 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
// });
|
||||
|
||||
menus.push({
|
||||
id: 'undo',
|
||||
label: '撤销(Undo)',
|
||||
disabled: !store.canUndo,
|
||||
onSelect: () => store.undo()
|
||||
});
|
||||
|
||||
menus.push({
|
||||
id: 'redo',
|
||||
label: '重做(Redo)',
|
||||
disabled: !store.canRedo,
|
||||
onSelect: () => store.redo()
|
||||
@ -499,6 +533,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
if (first.childRegions.length && renderersPanel) {
|
||||
if (first.childRegions.length > 1) {
|
||||
menus.push({
|
||||
id: 'insert',
|
||||
label: '插入组件',
|
||||
children: first.childRegions.map(region => ({
|
||||
label: `${region.label}`,
|
||||
@ -510,6 +545,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
});
|
||||
} else {
|
||||
menus.push({
|
||||
id: 'insert',
|
||||
label: '插入组件',
|
||||
data: first.childRegions[0].region,
|
||||
onHighlight: (isOn: boolean, region: string) =>
|
||||
@ -520,6 +556,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
}
|
||||
|
||||
menus.push({
|
||||
id: 'replace',
|
||||
label: '替换组件',
|
||||
disabled:
|
||||
!node.host ||
|
||||
@ -536,6 +573,7 @@ export class BasicToolbarPlugin extends BasePlugin {
|
||||
(info.plugin.scaffoldForm?.canRebuild || info.scaffoldForm?.canRebuild)
|
||||
) {
|
||||
menus.push({
|
||||
id: 'build',
|
||||
label: `快速构建「${info.plugin.name}」`,
|
||||
disabled: schema.$$commonSchema,
|
||||
onSelect: () =>
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
getVariable,
|
||||
mapObject,
|
||||
mapTree,
|
||||
eachTree,
|
||||
extendObject,
|
||||
createObject
|
||||
} from 'amis-core';
|
||||
@ -133,7 +134,6 @@ export const MainStore = types
|
||||
id: 'root',
|
||||
label: 'Root'
|
||||
}),
|
||||
map: types.optional(types.frozen(), {}),
|
||||
theme: 'cxd', // 主题,默认cxd主题
|
||||
hoverId: '',
|
||||
hoverRegion: '',
|
||||
@ -389,29 +389,7 @@ export const MainStore = types
|
||||
id: string,
|
||||
regionOrType?: string
|
||||
): EditorNodeType | undefined {
|
||||
const key = id + (regionOrType ? '-' + regionOrType : '');
|
||||
return self.map[key];
|
||||
|
||||
// let pool = self.root.children.concat();
|
||||
|
||||
// while (pool.length) {
|
||||
// const item = pool.shift();
|
||||
// if (
|
||||
// item.id === id &&
|
||||
// (!regionOrType ||
|
||||
// item.region === regionOrType ||
|
||||
// item.type === regionOrType)
|
||||
// ) {
|
||||
// return item;
|
||||
// }
|
||||
|
||||
// // 将当前节点的子节点全部放置到 pool中
|
||||
// if (item.children.length) {
|
||||
// pool.push.apply(pool, item.children);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return undefined;
|
||||
return self.root.getNodeById(id, regionOrType);
|
||||
},
|
||||
|
||||
get activeNodeInfo(): RendererInfo | null | undefined {
|
||||
@ -1058,36 +1036,6 @@ export const MainStore = types
|
||||
);
|
||||
|
||||
return {
|
||||
setNode(node: EditorNodeType) {
|
||||
const map = {...self.map};
|
||||
|
||||
if (node.region) {
|
||||
map[node.id + '-' + node.region] = node;
|
||||
} else {
|
||||
// 同名类型不同的节点,优先使用上层的
|
||||
// 因为原来的 getNodeById 是这种查找策略
|
||||
// 所以孩子节点在父级写入了的情况下不写入
|
||||
map[node.id] = map[node.id] || node;
|
||||
map[node.id + '-' + node.type] = node;
|
||||
}
|
||||
|
||||
self.map = map;
|
||||
},
|
||||
unsetNode(node: EditorNodeType) {
|
||||
const map = {...self.map};
|
||||
|
||||
if (node.region) {
|
||||
map[node.id + '-' + node.region] === node &&
|
||||
delete map[node.id + '-' + node.region];
|
||||
} else {
|
||||
map[node.id] === node && delete map[node.id];
|
||||
map[node.id + '-' + node.type] === node &&
|
||||
delete map[node.id + '-' + node.type];
|
||||
}
|
||||
|
||||
self.map = map;
|
||||
},
|
||||
|
||||
setLayer(value: any) {
|
||||
layer = value;
|
||||
},
|
||||
@ -1987,7 +1935,6 @@ export const MainStore = types
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
self.map = {};
|
||||
lazyUpdateTargetName.cancel();
|
||||
}
|
||||
};
|
||||
|
@ -77,6 +77,32 @@ export const EditorNode = types
|
||||
return info;
|
||||
},
|
||||
|
||||
getNodeById(id: string, regionOrType?: string) {
|
||||
// 找不到,再从 root.children 递归找
|
||||
let pool = self.children.concat();
|
||||
let resolved: any = undefined;
|
||||
|
||||
while (pool.length) {
|
||||
const item = pool.shift();
|
||||
if (
|
||||
item.id === id &&
|
||||
(!regionOrType ||
|
||||
item.region === regionOrType ||
|
||||
item.type === regionOrType)
|
||||
) {
|
||||
resolved = item;
|
||||
break;
|
||||
}
|
||||
|
||||
// 将当前节点的子节点全部放置到 pool中
|
||||
if (item.children.length) {
|
||||
pool.push.apply(pool, item.uniqueChildren);
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
},
|
||||
|
||||
setInfo(value: RendererInfo) {
|
||||
info = value;
|
||||
},
|
||||
@ -617,7 +643,6 @@ export const EditorNode = types
|
||||
});
|
||||
const node = self.children[self.children.length - 1];
|
||||
node.setInfo(props.info);
|
||||
(getRoot(self) as any).setNode(node);
|
||||
return node;
|
||||
},
|
||||
|
||||
@ -628,24 +653,6 @@ export const EditorNode = types
|
||||
return;
|
||||
}
|
||||
|
||||
// 因为 react 的钩子是 父级先执行 willUnmout,所以顶级的节点先删除
|
||||
// 节点删除了,再去读取 mst 又会报错
|
||||
// 所以在节点删除之前,先把所有孩子节点从 root.map 中删除
|
||||
// 否则 root.map 里面会残存很多已经销毁的节点
|
||||
const pool = [node];
|
||||
const list = [];
|
||||
|
||||
while (pool.length) {
|
||||
const item = pool.shift();
|
||||
list.push(item);
|
||||
pool.push(...item.children);
|
||||
}
|
||||
|
||||
const root = getRoot(self) as any;
|
||||
list.forEach((item: any) => {
|
||||
root.unsetNode(item);
|
||||
});
|
||||
|
||||
self.children.splice(idx, 1);
|
||||
},
|
||||
|
||||
|
@ -150,15 +150,18 @@ export class Evaluator {
|
||||
|
||||
// 只给简单的变量取值用法自动补fitler
|
||||
if (defaultFilter && ~['getter', 'variable'].indexOf(ast.body?.type)) {
|
||||
ast.body = {
|
||||
type: 'filter',
|
||||
input: ast.body,
|
||||
filters: [
|
||||
{
|
||||
name: defaultFilter.replace(/^\s*\|\s*/, ''),
|
||||
args: []
|
||||
}
|
||||
]
|
||||
ast = {
|
||||
...ast,
|
||||
body: {
|
||||
type: 'filter',
|
||||
input: ast.body,
|
||||
filters: [
|
||||
{
|
||||
name: defaultFilter.replace(/^\s*\|\s*/, ''),
|
||||
args: []
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ interface ContextMenuProps {
|
||||
}
|
||||
|
||||
export type MenuItem = {
|
||||
id?: string;
|
||||
label: string;
|
||||
icon?: string;
|
||||
disabled?: boolean;
|
||||
|
@ -223,7 +223,7 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
|
||||
return (
|
||||
<PopOverContainer
|
||||
mobileUI={mobileUI}
|
||||
disabled={operators.length < 2}
|
||||
disabled={!!(value?.op && operators.length < 2)}
|
||||
popOverContainer={popOverContainer || (() => findDOMNode(this))}
|
||||
popOverRender={({onClose}) => (
|
||||
<GroupedSelection
|
||||
|
@ -615,7 +615,7 @@ addSchemaFilter(function (schema: Schema, renderer: any, props: any) {
|
||||
|
||||
if (
|
||||
newSchema !== schema &&
|
||||
isObjectShallowModified(newSchema, schema, false)
|
||||
isObjectShallowModified(newSchema, schema, false, false, [], 10)
|
||||
) {
|
||||
return newSchema;
|
||||
}
|
||||
|
@ -76,7 +76,6 @@ export default function Cell({
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
<Checkbox
|
||||
@ -93,7 +92,6 @@ export default function Cell({
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName, {
|
||||
'is-dragDisabled': !item.draggable
|
||||
})}
|
||||
@ -105,7 +103,6 @@ export default function Cell({
|
||||
return (
|
||||
<td
|
||||
style={style}
|
||||
key={props.key}
|
||||
className={cx(column.pristine.className, stickyClassName)}
|
||||
>
|
||||
{item.expandable ? (
|
||||
|
Loading…
Reference in New Issue
Block a user