feat(editor,schema): 支持组件显示条件配置

This commit is contained in:
roymondchen 2023-08-04 15:52:55 +08:00
parent 0d3cd11ade
commit 35862078b3
12 changed files with 537 additions and 267 deletions

View File

@ -0,0 +1,56 @@
<template>
<m-form-container
:config="{
...config,
...cascaderConfig,
}"
:model="model"
@change="onChangeHandler"
></m-form-container>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { DataSourceFieldSelectConfig, Services } from '@editor/type';
const services = inject<Services>('services');
const emit = defineEmits(['change']);
const props = withDefaults(
defineProps<{
config: DataSourceFieldSelectConfig;
model: any;
lastValues?: any;
prop: string;
name: string;
disabled?: boolean;
size: 'small' | 'default' | 'large';
}>(),
{
disabled: false,
},
);
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
const cascaderConfig = {
type: 'cascader',
name: props.name,
options: () =>
dataSources.value
?.filter((ds) => ds.fields?.length)
?.map((ds) => ({
label: ds.title || ds.id,
value: ds.id,
children: ds.fields?.map((field) => ({
label: field.title,
value: field.name,
})),
})) || [],
};
const onChangeHandler = (value: any) => {
emit('change', value);
};
</script>

View File

@ -105,7 +105,7 @@ const cascaderConfig = {
dataSources.value
?.filter((ds) => ds.methods?.length)
?.map((ds) => ({
label: `${ds.title}${ds.id}`,
label: ds.title || ds.id,
value: ds.id,
children: ds.methods?.map((method) => ({
label: method.name,

View File

@ -22,6 +22,7 @@ import CodeLink from './fields/CodeLink.vue';
import CodeSelect from './fields/CodeSelect.vue';
import CodeSelectCol from './fields/CodeSelectCol.vue';
import DataSourceFields from './fields/DataSourceFields.vue';
import DataSourceFieldSelect from './fields/DataSourceFieldSelect.vue';
import DataSourceInput from './fields/DataSourceInput.vue';
import DataSourceMethods from './fields/DataSourceMethods.vue';
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
@ -59,6 +60,7 @@ export { default as DataSourceMethods } from './fields/DataSourceMethods.vue';
export { default as DataSourceInput } from './fields/DataSourceInput.vue';
export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
export { default as DataSourceMethodSelect } from './fields/DataSourceMethodSelect.vue';
export { default as DataSourceFieldSelect } from './fields/DataSourceFieldSelect.vue';
export { default as EventSelect } from './fields/EventSelect.vue';
export { default as KeyValue } from './fields/KeyValue.vue';
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
@ -98,5 +100,6 @@ export default {
app.component('m-fields-data-source-select', DataSourceSelect);
app.component('m-fields-data-source-methods', DataSourceMethods);
app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
app.component('m-fields-data-source-field-select', DataSourceFieldSelect);
},
};

View File

@ -7,9 +7,14 @@ import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from
import { getNodes } from '@tmagic/utils';
import type { Target } from './services/dep';
import { createCodeBlockTarget, createDataSourceTarget } from './utils/dep';
import {
createCodeBlockTarget,
createDataSourceCondTarget,
createDataSourceMethodTarget,
createDataSourceTarget,
} from './utils/dep';
import editorProps from './editorProps';
import type { Services } from './type';
import { DepTargetType, Services } from './type';
export declare type LooseRequired<T> = {
[P in string & keyof T]: T[P];
@ -139,6 +144,7 @@ export const initServiceEvents = (
if (app.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
app.dsl.dataSourceCondDeps = root.dataSourceCondDeps;
app.dsl.dataSources = root.dataSources;
}
@ -159,23 +165,34 @@ export const initServiceEvents = (
};
const targetAddHandler = (target: Target) => {
if (target.type !== 'data-source') return;
const root = editorService.get('root');
if (!root) return;
if (!root.dataSourceDeps) {
root.dataSourceDeps = {};
if (target.type === DepTargetType.DATA_SOURCE) {
if (!root.dataSourceDeps) {
root.dataSourceDeps = {};
}
root.dataSourceDeps[target.id] = target.deps;
}
root.dataSourceDeps[target.id] = target.deps;
if (target.type === DepTargetType.DATA_SOURCE_COND) {
if (!root.dataSourceCondDeps) {
root.dataSourceCondDeps = {};
}
root.dataSourceCondDeps[target.id] = target.deps;
}
};
const targetRemoveHandler = (id: string | number) => {
const root = editorService.get('root');
if (!root?.dataSourceDeps) return;
delete root.dataSourceDeps[id];
if (root?.dataSourceDeps) {
delete root.dataSourceDeps[id];
}
if (root?.dataSourceCondDeps) {
delete root.dataSourceCondDeps[id];
}
};
const depUpdateHandler = (node: MNode) => {
@ -191,6 +208,12 @@ export const initServiceEvents = (
depService.on('dep-update', depUpdateHandler);
depService.on('collected', collectedHandler);
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
depService.addTarget(createDataSourceTarget(ds.id));
depService.addTarget(createDataSourceMethodTarget(ds.id));
depService.addTarget(createDataSourceCondTarget(ds.id));
};
const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
const nodeId = editorService.get('node')?.id || props.defaultSelected;
let node;
@ -218,14 +241,14 @@ export const initServiceEvents = (
codeBlockService.setCodeDsl(value.codeBlocks);
dataSourceService.set('dataSources', value.dataSources);
depService.removeTargets('code-block');
depService.removeTargets(DepTargetType.CODE_BLOCK);
Object.entries(value.codeBlocks).forEach(([id, code]) => {
depService.addTarget(createCodeBlockTarget(id, code));
});
value.dataSources.forEach((ds) => {
depService.addTarget(createDataSourceTarget(ds.id, ds));
initDataSourceDepTarget(ds);
});
if (value && Array.isArray(value.items)) {
@ -279,17 +302,14 @@ export const initServiceEvents = (
codeBlockService.on('remove', codeBlockRemoveHandler);
const dataSourceAddHandler = (config: DataSourceSchema) => {
depService.addTarget(createDataSourceTarget(config.id, config));
initDataSourceDepTarget(config);
getApp()?.dataSourceManager?.addDataSource(config);
};
const dataSourceUpdateHandler = (config: DataSourceSchema) => {
if (config.title) {
depService.getTarget(config.id)!.name = config.title;
}
const root = editorService.get('root');
const targets = depService.getTargets('data-source');
const targets = depService.getTargets(DepTargetType.DATA_SOURCE);
const nodes = getNodes(Object.keys(targets[config.id].deps), root?.items);

View File

@ -50,7 +50,7 @@ import type { Id } from '@tmagic/schema';
import Icon from '@editor/components/Icon.vue';
import AppManageIcon from '@editor/icons/AppManageIcon.vue';
import CodeIcon from '@editor/icons/CodeIcon.vue';
import { CodeDeleteErrorType, CodeDslItem, Services } from '@editor/type';
import { CodeDeleteErrorType, CodeDslItem, DepTargetType, Services } from '@editor/type';
defineOptions({
name: 'MEditorCodeBlockList',
@ -69,7 +69,7 @@ const { codeBlockService, depService, editorService } = inject<Services>('servic
//
const codeList = computed(() =>
Object.values(depService?.targets['code-block'] || {}).map((target) => {
Object.values(depService?.getTargets(DepTargetType.CODE_BLOCK) || {}).map((target) => {
//
const compNodes = Object.entries(target.deps).map(([id, dep]) => ({
name: dep.name,

View File

@ -15,11 +15,11 @@
<div class="list-item">
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
>{{ data.name }}{{ data.id }}</span
>
<span class="name" :class="{ code: data.type === 'ds', hook: data.type === 'key' }">{{
data.type === 'key' ? data.name : `${data.name}(${data.id})`
}}</span>
<!-- 右侧工具栏 -->
<div class="right-tool" v-if="data.type === 'code'">
<div class="right-tool" v-if="data.type === 'ds'">
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.id}`)"></Icon>
</TMagicTooltip>
@ -39,10 +39,10 @@ import { computed, inject, ref } from 'vue';
import { Aim, Close, Coin, Edit, View } from '@element-plus/icons-vue';
import { tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
import { Id } from '@tmagic/schema';
import { Dep, Id } from '@tmagic/schema';
import Icon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type';
import { DepTargetType, Services } from '@editor/type';
defineOptions({
name: 'MEditorDataSourceList',
@ -57,18 +57,52 @@ const { depService, editorService, dataSourceService } = inject<Services>('servi
const editable = computed(() => dataSourceService?.get('editable') ?? true);
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
const dsDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE) || {});
const dsMethodDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_METHOD) || {});
const dsCondDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_COND) || {});
const getKeyTreeConfig = (dep: Dep[string], type?: string) =>
dep.keys.map((key) => ({ name: key, id: key, type: 'key', isMethod: type === 'method', isCond: type === 'cond' }));
const getNodeTreeConfig = (id: string, dep: Dep[string], type?: string) => ({
name: dep.name,
type: 'node',
id,
children: getKeyTreeConfig(dep, type),
});
const list = computed(() =>
Object.values(depService?.targets['data-source'] || {}).map((target) => ({
id: target.id,
name: target.name,
type: 'code',
children: Object.entries(target.deps).map(([id, dep]) => ({
name: dep.name,
type: 'node',
id,
children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
})),
})),
dataSources.value.map((ds) => {
const dsDeps = dsDep.value[ds.id].deps;
const dsMethodDeps = dsMethodDep.value[ds.id].deps;
const dsCondDeps = dsCondDep.value[ds.id].deps;
const children: any[] = [];
const mergeChildren = (deps: Dep, type?: string) => {
Object.entries(deps).forEach(([id, dep]) => {
const nodeItem = children.find((item) => item.id === id);
if (nodeItem) {
nodeItem.children = nodeItem.children.concat(getKeyTreeConfig(dep, type));
} else {
children.push(getNodeTreeConfig(id, dep, type));
}
});
};
mergeChildren(dsDeps);
mergeChildren(dsMethodDeps, 'method');
mergeChildren(dsCondDeps, 'cond');
return {
id: ds.id,
name: ds.title,
type: 'ds',
children,
};
}),
);
const editHandler = (id: string) => {

View File

@ -19,27 +19,24 @@ import { EventEmitter } from 'events';
import { reactive } from 'vue';
import { Id, MNode } from '@tmagic/schema';
import type { Dep, Id, MNode } from '@tmagic/schema';
import { isObject } from '@tmagic/utils';
import { DepTargetType } from '@editor/type';
type IsTarget = (key: string | number, value: any) => boolean;
interface TargetOptions {
isTarget: IsTarget;
id: string | number;
type?: string;
name: string;
}
interface Dep {
[key: string | number]: {
name: string;
keys: (string | number)[];
};
/** 类型,数据源、代码块或其他 */
type?: DepTargetType | string;
name?: string;
}
interface TargetList {
[key: string]: {
[key: string | number]: Target;
[type: DepTargetType | string]: {
[targetId: string | number]: Target;
};
}
@ -60,11 +57,11 @@ export class Target extends EventEmitter {
/**
*
*/
public name: string;
public name?: string;
/**
* type
*/
public type = 'default';
public type: DepTargetType | string = DepTargetType.DEFAULT;
/**
*
* { 'node_id': { name: 'node_name', keys: [ created, mounted ] } }
@ -156,14 +153,14 @@ export class Target extends EventEmitter {
}
export class Watcher extends EventEmitter {
public targets = reactive<TargetList>({});
private targets = reactive<TargetList>({});
/**
* target
* @param type
* @returns Target[]
*/
public getTargets(type = 'default') {
public getTargets(type: DepTargetType | string = DepTargetType.DEFAULT) {
return this.targets[type] || {};
}
@ -230,7 +227,7 @@ export class Watcher extends EventEmitter {
* @param type
* @returns void
*/
public removeTargets(type = 'default') {
public removeTargets(type: DepTargetType | string = DepTargetType.DEFAULT) {
const targets = this.targets[type];
if (!targets) return;
@ -307,9 +304,11 @@ export class Watcher extends EventEmitter {
this.emit('update-dep', node, fullKey);
} else if (!keyIsItems && Array.isArray(value)) {
value.forEach((item, index) => {
collectTarget(item, `${fullKey}.${index}`);
if (isObject(item)) {
collectTarget(item, `${fullKey}.${index}`);
}
});
} else if (Object.prototype.toString.call(value) === '[object Object]') {
} else if (isObject(value)) {
collectTarget(value, fullKey);
}
@ -321,6 +320,7 @@ export class Watcher extends EventEmitter {
};
Object.entries(config).forEach(([key, value]) => {
if (typeof value === 'undefined' || value === '') return;
doCollect(key, value);
});
};

View File

@ -500,3 +500,23 @@ export interface DataSourceMethodSelectConfig {
disabled?: boolean | FilterFunction;
display?: boolean | FilterFunction;
}
export interface DataSourceFieldSelectConfig {
type: 'data-source-field-select';
name: string;
labelWidth?: number | string;
disabled?: boolean | FilterFunction;
display?: boolean | FilterFunction;
}
export enum DepTargetType {
DEFAULT = 'default',
/** 代码块 */
CODE_BLOCK = 'code-block',
/** 数据源 */
DATA_SOURCE = 'data-source',
/** 数据源方法 */
DATA_SOURCE_METHOD = 'data-source-method',
/** 数据源条件 */
DATA_SOURCE_COND = 'data-source-cond',
}

View File

@ -1,13 +1,14 @@
import { isEmpty } from 'lodash-es';
import { CodeBlockContent, DataSourceSchema, HookType, Id } from '@tmagic/schema';
import { CodeBlockContent, HookType, Id } from '@tmagic/schema';
import dataSourceService from '@editor/services/dataSource';
import { Target } from '@editor/services/dep';
import type { HookData } from '@editor/type';
import { DepTargetType, HookData } from '@editor/type';
export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent) =>
new Target({
type: 'code-block',
type: DepTargetType.CODE_BLOCK,
id,
name: codeBlock.name,
isTarget: (key: string | number, value: any) => {
@ -24,12 +25,37 @@ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent) =>
},
});
export const createDataSourceTarget = (id: Id, ds: DataSourceSchema) =>
export const createDataSourceTarget = (id: Id) =>
new Target({
type: 'data-source',
type: DepTargetType.DATA_SOURCE,
id,
name: ds.title || `${id}`,
isTarget: (key: string | number, value: any) =>
// 关联数据源对象或者在模板在使用数据源
(value.isBindDataSource && value.dataSourceId) || (typeof value === 'string' && value.includes(`${id}`)),
(value?.isBindDataSource && value.dataSourceId) || (typeof value === 'string' && value.includes(`${id}`)),
});
export const createDataSourceCondTarget = (id: string) =>
new Target({
type: DepTargetType.DATA_SOURCE_COND,
id,
isTarget: (key: string | number, value: any) => {
if (!Array.isArray(value) || value[0] !== id) return false;
const ds = dataSourceService.getDataSourceById(id);
return Boolean(ds?.fields?.find((field) => field.name === value[1]));
},
});
export const createDataSourceMethodTarget = (id: string) =>
new Target({
type: DepTargetType.DATA_SOURCE_METHOD,
id,
isTarget: (key: string | number, value: any) => {
if (!Array.isArray(value) || value[0] !== id) return false;
const ds = dataSourceService.getDataSourceById(id);
return Boolean(ds?.methods?.find((method) => method.name === value[1]));
},
});

View File

@ -16,7 +16,300 @@
* limitations under the License.
*/
import { FormConfig, FormState } from '@tmagic/form';
import type { FormConfig, FormState, TabPaneConfig } from '@tmagic/form';
import dataSourceService from '@editor/services/dataSource';
const arrayOptions = [
{ text: '包含', value: 'include' },
{ text: '不包含', value: 'not_include' },
];
const eqOptions = [
{ text: '等于', value: '=' },
{ text: '不等于', value: '!=' },
];
const numberOptions = [
{ text: '大于', value: '>' },
{ text: '大于等于', value: '>=' },
{ text: '小于', value: '<' },
{ text: '小于等于', value: '<=' },
{ text: '在范围内', value: 'between' },
{ text: '不在范围内', value: 'not_between' },
];
export const styleTabConfig: TabPaneConfig = {
title: '样式',
labelWidth: '80px',
items: [
{
name: 'style',
items: [
{
type: 'fieldset',
legend: '位置',
items: [
{
name: 'position',
type: 'checkbox',
activeValue: 'fixed',
inactiveValue: 'absolute',
defaultValue: 'absolute',
text: '固定定位',
},
{
name: 'left',
text: 'left',
},
{
name: 'top',
text: 'top',
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedBottom',
},
{
name: 'right',
text: 'right',
},
{
name: 'bottom',
text: 'bottom',
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedTop',
},
],
},
{
type: 'fieldset',
legend: '盒子',
items: [
{
name: 'width',
text: '宽度',
},
{
name: 'height',
text: '高度',
},
],
},
{
type: 'fieldset',
legend: '边框',
items: [
{
name: 'borderWidth',
text: '宽度',
defaultValue: '0',
},
{
name: 'borderColor',
text: '颜色',
type: 'colorPicker',
},
{
name: 'borderStyle',
text: '样式',
type: 'select',
defaultValue: 'none',
options: [
{ text: 'none', value: 'none' },
{ text: 'hidden', value: 'hidden' },
{ text: 'dotted', value: 'dotted' },
{ text: 'dashed', value: 'dashed' },
{ text: 'solid', value: 'solid' },
{ text: 'double', value: 'double' },
{ text: 'groove', value: 'groove' },
{ text: 'ridge', value: 'ridge' },
{ text: 'inset', value: 'inset' },
{ text: 'outset', value: 'outset' },
],
},
],
},
{
type: 'fieldset',
legend: '背景',
items: [
{
name: 'backgroundImage',
text: '背景图',
},
{
name: 'backgroundColor',
text: '背景颜色',
type: 'colorPicker',
},
{
name: 'backgroundRepeat',
text: '背景图重复',
type: 'select',
defaultValue: 'no-repeat',
options: [
{ text: 'repeat', value: 'repeat' },
{ text: 'repeat-x', value: 'repeat-x' },
{ text: 'repeat-y', value: 'repeat-y' },
{ text: 'no-repeat', value: 'no-repeat' },
{ text: 'inherit', value: 'inherit' },
],
},
{
name: 'backgroundSize',
text: '背景图大小',
defaultValue: '100% 100%',
},
],
},
{
type: 'fieldset',
legend: '字体',
items: [
{
name: 'color',
text: '颜色',
type: 'colorPicker',
},
{
name: 'fontSize',
text: '大小',
},
{
name: 'fontWeight',
text: '粗细',
},
],
},
{
type: 'fieldset',
legend: '变形',
name: 'transform',
items: [
{
name: 'rotate',
text: '旋转角度',
},
{
name: 'scale',
text: '缩放',
},
],
},
],
},
],
};
export const eventTabConfig: TabPaneConfig = {
title: '事件',
items: [
{
name: 'events',
type: 'event-select',
},
],
};
export const advancedTabConfig: TabPaneConfig = {
title: '高级',
lazy: true,
items: [
{
name: 'created',
text: 'created',
labelWidth: '100px',
type: 'code-select',
},
{
name: 'mounted',
text: 'mounted',
labelWidth: '100px',
type: 'code-select',
},
],
};
export const displayTabConfig: TabPaneConfig = {
title: '显示条件',
display: (vm: FormState, { model }: any) => model.type !== 'page',
items: [
{
type: 'groupList',
name: 'showCond',
items: [
{
type: 'table',
name: 'cond',
items: [
{
type: 'data-source-field-select',
name: 'field',
label: '字段',
},
{
type: 'select',
options: (mForm: FormState | undefined, { model }: any) => {
const [id, field] = model.field;
const ds = dataSourceService.getDataSourceById(id);
const type = ds?.fields.find((f) => f.name === field)?.type;
if (type === 'array') {
return arrayOptions;
}
if (type === 'boolean') {
return [
{ text: '是', value: 'is' },
{ text: '不是', value: 'not' },
];
}
if (type === 'number') {
return [...eqOptions, ...numberOptions];
}
if (type === 'string') {
return [...arrayOptions, ...eqOptions];
}
return [...arrayOptions, ...eqOptions, ...numberOptions];
},
label: '条件',
name: 'op',
},
{
label: '值',
items: [
{
name: 'value',
display: (vm: FormState, { model }: any) =>
!['between', 'not_between', 'is', 'not'].includes(model.op),
},
{
name: 'value',
type: 'select',
options: [
{ text: 'true', value: true },
{ text: 'false', value: false },
],
display: (vm: FormState, { model }: any) => ['is', 'not'].includes(model.op),
},
{
name: 'range',
type: 'number-range',
display: (vm: FormState, { model }: any) =>
['between', 'not_between'].includes(model.op) && !['is', 'not'].includes(model.op),
},
],
},
],
},
],
},
],
};
/**
*
@ -50,194 +343,10 @@ export const fillConfig = (config: FormConfig = []) => [
...config,
],
},
{
title: '样式',
labelWidth: '80px',
items: [
{
name: 'style',
items: [
{
type: 'fieldset',
legend: '位置',
items: [
{
name: 'position',
type: 'checkbox',
activeValue: 'fixed',
inactiveValue: 'absolute',
defaultValue: 'absolute',
text: '固定定位',
},
{
name: 'left',
text: 'left',
},
{
name: 'top',
text: 'top',
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedBottom',
},
{
name: 'right',
text: 'right',
},
{
name: 'bottom',
text: 'bottom',
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedTop',
},
],
},
{
type: 'fieldset',
legend: '盒子',
items: [
{
name: 'width',
text: '宽度',
},
{
name: 'height',
text: '高度',
},
],
},
{
type: 'fieldset',
legend: '边框',
items: [
{
name: 'borderWidth',
text: '宽度',
defaultValue: '0',
},
{
name: 'borderColor',
text: '颜色',
type: 'colorPicker',
},
{
name: 'borderStyle',
text: '样式',
type: 'select',
defaultValue: 'none',
options: [
{ text: 'none', value: 'none' },
{ text: 'hidden', value: 'hidden' },
{ text: 'dotted', value: 'dotted' },
{ text: 'dashed', value: 'dashed' },
{ text: 'solid', value: 'solid' },
{ text: 'double', value: 'double' },
{ text: 'groove', value: 'groove' },
{ text: 'ridge', value: 'ridge' },
{ text: 'inset', value: 'inset' },
{ text: 'outset', value: 'outset' },
],
},
],
},
{
type: 'fieldset',
legend: '背景',
items: [
{
name: 'backgroundImage',
text: '背景图',
},
{
name: 'backgroundColor',
text: '背景颜色',
type: 'colorPicker',
},
{
name: 'backgroundRepeat',
text: '背景图重复',
type: 'select',
defaultValue: 'no-repeat',
options: [
{ text: 'repeat', value: 'repeat' },
{ text: 'repeat-x', value: 'repeat-x' },
{ text: 'repeat-y', value: 'repeat-y' },
{ text: 'no-repeat', value: 'no-repeat' },
{ text: 'inherit', value: 'inherit' },
],
},
{
name: 'backgroundSize',
text: '背景图大小',
defaultValue: '100% 100%',
},
],
},
{
type: 'fieldset',
legend: '字体',
items: [
{
name: 'color',
text: '颜色',
type: 'colorPicker',
},
{
name: 'fontSize',
text: '大小',
},
{
name: 'fontWeight',
text: '粗细',
},
],
},
{
type: 'fieldset',
legend: '变形',
name: 'transform',
items: [
{
name: 'rotate',
text: '旋转角度',
},
{
name: 'scale',
text: '缩放',
},
],
},
],
},
],
},
{
title: '事件',
items: [
{
name: 'events',
type: 'event-select',
labelWidth: 0,
},
],
},
{
title: '高级',
lazy: true,
items: [
{
name: 'created',
text: 'created',
labelWidth: '100px',
type: 'code-select',
},
{
name: 'mounted',
text: 'mounted',
labelWidth: '100px',
type: 'code-select',
},
],
},
{ ...styleTabConfig },
{ ...eventTabConfig },
{ ...advancedTabConfig },
{ ...displayTabConfig },
],
},
];

View File

@ -122,10 +122,10 @@ describe('depService', () => {
const target1 = depService.getTarget('collect_1');
const target2 = depService.getTarget('collect_2');
expect((target1?.deps || {}).node_1.name).toBe('node');
expect((target2?.deps || {}).node_1.name).toBe('node');
expect((target1?.deps || {}).node_1.keys).toHaveLength(1);
expect((target2?.deps || {}).node_1.keys).toHaveLength(3);
expect(target1?.deps?.node_1.name).toBe('node');
expect(target2?.deps?.node_1.name).toBe('node');
expect(target1?.deps?.node_1.keys).toHaveLength(1);
expect(target2?.deps?.node_1.keys).toHaveLength(3);
depService.collect([
{
@ -146,8 +146,8 @@ describe('depService', () => {
},
]);
expect((target1?.deps || {}).node_1).toBeUndefined();
expect((target2?.deps || {}).node_1.keys).toHaveLength(1);
expect(target1?.deps?.node_1).toBeUndefined();
expect(target2?.deps?.node_1.keys).toHaveLength(1);
depService.collect([
{
@ -158,8 +158,8 @@ describe('depService', () => {
},
]);
expect((target1?.deps || {}).node_1).toBeUndefined();
expect((target2?.deps || {}).node_1.keys[0]).toBe('text1');
expect(target1?.deps?.node_1).toBeUndefined();
expect(target2?.deps?.node_1.keys[0]).toBe('text1');
depService.clear([
{
@ -168,8 +168,8 @@ describe('depService', () => {
},
]);
expect((target1?.deps || {}).node_1).toBeUndefined();
expect((target2?.deps || {}).node_1).toBeUndefined();
expect(target1?.deps?.node_1).toBeUndefined();
expect(target2?.deps?.node_1).toBeUndefined();
});
test('collect deep', () => {
@ -204,8 +204,8 @@ describe('depService', () => {
const target1 = depService.getTarget('collect_1');
expect((target1?.deps || {}).node_1.name).toBe('node');
expect((target1?.deps || {}).node_2.name).toBe('node2');
expect(target1?.deps?.node_1.name).toBe('node');
expect(target1?.deps?.node_2.name).toBe('node2');
depService.clear([
{
@ -221,7 +221,7 @@ describe('depService', () => {
},
]);
expect((target1?.deps || {}).node_1).toBeUndefined();
expect((target1?.deps || {}).node_2).toBeUndefined();
expect(target1?.deps?.node_1).toBeUndefined();
expect(target1?.deps?.node_2).toBeUndefined();
});
});

View File

@ -125,6 +125,7 @@ export interface MApp extends MComponent {
dataSources?: DataSourceSchema[];
dataSourceDeps?: DataSourceDeps;
dataSourceCondDeps?: DataSourceDeps;
}
export interface CodeBlockDSL {
@ -191,14 +192,15 @@ export interface DataSourceSchema {
/** 字段列表 */
fields: DataSchema[];
/** 方法列表 */
methods?: CodeBlockContent[];
methods: CodeBlockContent[];
/** 扩展字段 */
[key: string]: any;
}
export interface Dep {
[nodeId: Id]: {
/** 组件名称 */
name: string;
keys: Id[];
keys: (string | number)[];
};
}