mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor,schema): 支持组件显示条件配置
This commit is contained in:
parent
0d3cd11ade
commit
35862078b3
56
packages/editor/src/fields/DataSourceFieldSelect.vue
Normal file
56
packages/editor/src/fields/DataSourceFieldSelect.vue
Normal 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>
|
@ -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,
|
||||
|
@ -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);
|
||||
},
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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]));
|
||||
},
|
||||
});
|
||||
|
@ -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 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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)[];
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user