mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-04 14:59:48 +08:00
feat(dats-source,editor,form,schema,ui): 迭代器容器支持配置子项显示条件
This commit is contained in:
parent
0c5485b1d0
commit
a164e2be62
@ -20,13 +20,13 @@ import EventEmitter from 'events';
|
|||||||
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import type { AppCore, DataSourceSchema, Id, MNode } from '@tmagic/schema';
|
import type { AppCore, DataSourceSchema, DisplayCond, Id, MNode } from '@tmagic/schema';
|
||||||
import { compiledNode } from '@tmagic/utils';
|
import { compiledNode } from '@tmagic/utils';
|
||||||
|
|
||||||
import { SimpleObservedData } from './observed-data/SimpleObservedData';
|
import { SimpleObservedData } from './observed-data/SimpleObservedData';
|
||||||
import { DataSource, HttpDataSource } from './data-sources';
|
import { DataSource, HttpDataSource } from './data-sources';
|
||||||
import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions, ObservedDataClass } from './types';
|
import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions, ObservedDataClass } from './types';
|
||||||
import { compiledNodeField, compliedConditions, compliedIteratorItems } from './utils';
|
import { compiledNodeField, compliedConditions, compliedIteratorItemConditions, compliedIteratorItems } from './utils';
|
||||||
|
|
||||||
class DataSourceManager extends EventEmitter {
|
class DataSourceManager extends EventEmitter {
|
||||||
private static dataSourceClassMap = new Map<string, typeof DataSource>();
|
private static dataSourceClassMap = new Map<string, typeof DataSource>();
|
||||||
@ -214,6 +214,10 @@ class DataSourceManager extends EventEmitter {
|
|||||||
return compliedConditions(node, this.data);
|
return compliedConditions(node, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public compliedIteratorItemConds(itemData: any, displayConds: DisplayCond[] = []) {
|
||||||
|
return compliedIteratorItemConditions(displayConds, itemData);
|
||||||
|
}
|
||||||
|
|
||||||
public compliedIteratorItems(itemData: any, items: MNode[], dataSourceField: string[] = []) {
|
public compliedIteratorItems(itemData: any, items: MNode[], dataSourceField: string[] = []) {
|
||||||
const [dsId, ...keys] = dataSourceField;
|
const [dsId, ...keys] = dataSourceField;
|
||||||
const ds = this.get(dsId);
|
const ds = this.get(dsId);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { cloneDeep, template } from 'lodash-es';
|
import { cloneDeep, template } from 'lodash-es';
|
||||||
|
|
||||||
import { isDataSourceTemplate, isUseDataSourceField, Target, Watcher } from '@tmagic/dep';
|
import { isDataSourceTemplate, isUseDataSourceField, Target, Watcher } from '@tmagic/dep';
|
||||||
import type { MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
import type { DisplayCond, DisplayCondItem, MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||||
import {
|
import {
|
||||||
compiledCond,
|
compiledCond,
|
||||||
compiledNode,
|
compiledNode,
|
||||||
@ -15,29 +15,69 @@ import {
|
|||||||
|
|
||||||
import type { AsyncDataSourceResolveResult, DataSourceManagerData } from './types';
|
import type { AsyncDataSourceResolveResult, DataSourceManagerData } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译显示条件
|
||||||
|
* @param cond 条件配置
|
||||||
|
* @param data 上下文数据(数据源数据)
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
export const compiledCondition = (cond: DisplayCondItem[], data: DataSourceManagerData) => {
|
||||||
|
let result = true;
|
||||||
|
for (const { op, value, range, field } of cond) {
|
||||||
|
const [sourceId, ...fields] = field;
|
||||||
|
|
||||||
|
const dsData = data[sourceId];
|
||||||
|
|
||||||
|
if (!dsData || !fields.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldValue = getValueByKeyPath(fields.join('.'), dsData);
|
||||||
|
|
||||||
|
if (!compiledCond(op, fieldValue, value, range)) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译数据源条件组
|
* 编译数据源条件组
|
||||||
* @param node dsl节点
|
* @param node dsl节点
|
||||||
* @param data 数据源数据
|
* @param data 数据源数据
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
export const compliedConditions = (node: MNode, data: DataSourceManagerData) => {
|
export const compliedConditions = (node: { displayConds?: DisplayCond[] }, data: DataSourceManagerData) => {
|
||||||
if (!node.displayConds || !Array.isArray(node.displayConds) || !node.displayConds.length) return true;
|
if (!node.displayConds || !Array.isArray(node.displayConds) || !node.displayConds.length) return true;
|
||||||
|
|
||||||
for (const { cond } of node.displayConds) {
|
for (const { cond } of node.displayConds) {
|
||||||
if (!cond) continue;
|
if (!cond) continue;
|
||||||
|
|
||||||
|
if (compiledCondition(cond, data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译迭代器容器子项显示条件
|
||||||
|
* @param displayConds 条件组配置
|
||||||
|
* @param data 迭代器容器的迭代数据项
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
export const compliedIteratorItemConditions = (displayConds: DisplayCond[] = [], data: DataSourceManagerData) => {
|
||||||
|
if (!displayConds || !Array.isArray(displayConds) || !displayConds.length) return true;
|
||||||
|
|
||||||
|
for (const { cond } of displayConds) {
|
||||||
|
if (!cond) continue;
|
||||||
|
|
||||||
let result = true;
|
let result = true;
|
||||||
for (const { op, value, range, field } of cond) {
|
for (const { op, value, range, field } of cond) {
|
||||||
const [sourceId, ...fields] = field;
|
const fieldValue = getValueByKeyPath(field.join('.'), data);
|
||||||
|
|
||||||
const dsData = data[sourceId];
|
|
||||||
|
|
||||||
if (!dsData || !fields.length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldValue = getValueByKeyPath(fields.join('.'), data[sourceId]);
|
|
||||||
|
|
||||||
if (!compiledCond(op, fieldValue, value, range)) {
|
if (!compiledCond(op, fieldValue, value, range)) {
|
||||||
result = false;
|
result = false;
|
||||||
@ -45,9 +85,7 @@ export const compliedConditions = (node: MNode, data: DataSourceManagerData) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
return result;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
87
packages/editor/src/fields/CondOpSelect.vue
Normal file
87
packages/editor/src/fields/CondOpSelect.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<TMagicSelect
|
||||||
|
v-model="model[name]"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="fieldChangeHandler"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
v-for="option in options"
|
||||||
|
class="tmagic-design-option"
|
||||||
|
:key="option.value"
|
||||||
|
:is="optionComponent?.component || 'el-option'"
|
||||||
|
v-bind="
|
||||||
|
optionComponent?.props({
|
||||||
|
label: option.text,
|
||||||
|
value: option.value,
|
||||||
|
}) || {
|
||||||
|
label: option.text,
|
||||||
|
value: option.value,
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</component>
|
||||||
|
</TMagicSelect>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject } from 'vue';
|
||||||
|
|
||||||
|
import { getConfig as getDesignConfig, TMagicSelect } from '@tmagic/design';
|
||||||
|
import type { FieldProps } from '@tmagic/form';
|
||||||
|
|
||||||
|
import type { CondOpSelectConfig, Services } from '@editor/type';
|
||||||
|
import { arrayOptions, eqOptions, numberOptions } from '@editor/utils';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MFieldsCondOpSelect',
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const { dataSourceService } = inject<Services>('services') || {};
|
||||||
|
|
||||||
|
const props = defineProps<FieldProps<CondOpSelectConfig>>();
|
||||||
|
|
||||||
|
const optionComponent = getDesignConfig('components')?.option;
|
||||||
|
|
||||||
|
const options = computed(() => {
|
||||||
|
const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field];
|
||||||
|
|
||||||
|
const ds = dataSourceService?.getDataSourceById(id);
|
||||||
|
|
||||||
|
let fields = ds?.fields || [];
|
||||||
|
let type = '';
|
||||||
|
(fieldNames || []).forEach((fieldName: string) => {
|
||||||
|
const field = fields.find((f) => f.name === fieldName);
|
||||||
|
fields = field?.fields || [];
|
||||||
|
type = 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];
|
||||||
|
});
|
||||||
|
|
||||||
|
const fieldChangeHandler = (v: string[]) => {
|
||||||
|
emit('change', v);
|
||||||
|
};
|
||||||
|
</script>
|
@ -137,7 +137,7 @@ const dsChangeHandler = (v: string) => {
|
|||||||
emit('change', modelValue.value);
|
emit('change', modelValue.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldChangeHandler = (v: string[]) => {
|
const fieldChangeHandler = (v: string[] = []) => {
|
||||||
modelValue.value = [selectDataSourceId.value, ...v];
|
modelValue.value = [selectDataSourceId.value, ...v];
|
||||||
emit('change', modelValue.value);
|
emit('change', modelValue.value);
|
||||||
};
|
};
|
||||||
|
@ -129,10 +129,20 @@ const checkStrictly = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onChangeHandler = (value: string[]) => {
|
const onChangeHandler = (value: string[]) => {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
emit('change', value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [dsId, ...keys] = value;
|
const [dsId, ...keys] = value;
|
||||||
const dataSource = dataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(dsId));
|
const dataSource = dataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(dsId));
|
||||||
|
|
||||||
let fields = dataSource?.fields || [];
|
if (!dataSource) {
|
||||||
|
emit('change', value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = dataSource.fields || [];
|
||||||
let field: DataSchema | undefined;
|
let field: DataSchema | undefined;
|
||||||
(keys || []).forEach((key) => {
|
(keys || []).forEach((key) => {
|
||||||
field = fields.find((f) => f.name === key);
|
field = fields.find((f) => f.name === key);
|
||||||
@ -145,7 +155,6 @@ const onChangeHandler = (value: string[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!dsId ||
|
|
||||||
!keys.length ||
|
!keys.length ||
|
||||||
(field?.type &&
|
(field?.type &&
|
||||||
(field.type === 'any' || dataSourceFieldType.includes('any') || dataSourceFieldType.includes(field.type)))
|
(field.type === 'any' || dataSourceFieldType.includes('any') || dataSourceFieldType.includes(field.type)))
|
||||||
|
@ -172,6 +172,12 @@ const dataSourceFieldsConfig: FormConfig = [
|
|||||||
{ text: 'null', value: 'null' },
|
{ text: 'null', value: 'null' },
|
||||||
{ text: 'any', value: 'any' },
|
{ text: 'any', value: 'any' },
|
||||||
],
|
],
|
||||||
|
onChange: (formState, v: string, { model }) => {
|
||||||
|
if (!['any', 'array', 'object'].includes(v)) {
|
||||||
|
model.fields = [];
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
145
packages/editor/src/fields/DisplayConds.vue
Normal file
145
packages/editor/src/fields/DisplayConds.vue
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<MGroupList
|
||||||
|
style="width: 100%"
|
||||||
|
:config="config"
|
||||||
|
:name="name"
|
||||||
|
:disabled="disabled"
|
||||||
|
:model="model"
|
||||||
|
:last-values="lastValues"
|
||||||
|
:prop="prop"
|
||||||
|
:size="size"
|
||||||
|
></MGroupList>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, inject } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
type FieldProps,
|
||||||
|
type FilterFunction,
|
||||||
|
filterFunction,
|
||||||
|
type FormState,
|
||||||
|
type GroupListConfig,
|
||||||
|
MGroupList,
|
||||||
|
} from '@tmagic/form';
|
||||||
|
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
import { getCascaderOptionsFromFields } from '@editor/utils';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'm-fields-display-conds',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<
|
||||||
|
FieldProps<{
|
||||||
|
titlePrefix?: string;
|
||||||
|
parentFields?: string[] | FilterFunction<string[]>;
|
||||||
|
}>
|
||||||
|
>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { dataSourceService } = inject<Services>('services') || {};
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
const parentFields = computed(() => filterFunction<string[]>(mForm, props.config.parentFields, props) || []);
|
||||||
|
|
||||||
|
const config = computed<GroupListConfig>(() => ({
|
||||||
|
type: 'groupList',
|
||||||
|
name: props.name,
|
||||||
|
titlePrefix: props.config.titlePrefix,
|
||||||
|
expandAll: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'table',
|
||||||
|
name: 'cond',
|
||||||
|
operateColWidth: 50,
|
||||||
|
items: [
|
||||||
|
parentFields.value.length
|
||||||
|
? {
|
||||||
|
type: 'cascader',
|
||||||
|
options: () => {
|
||||||
|
const [dsId, ...keys] = parentFields.value;
|
||||||
|
const ds = dataSourceService?.getDataSourceById(dsId);
|
||||||
|
if (!ds) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = ds.fields || [];
|
||||||
|
keys.forEach((key) => {
|
||||||
|
const field = fields.find((f) => f.name === key);
|
||||||
|
fields = field?.fields || [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return getCascaderOptionsFromFields(fields, ['string', 'number', 'boolean', 'any']);
|
||||||
|
},
|
||||||
|
name: 'field',
|
||||||
|
value: 'key',
|
||||||
|
label: '字段',
|
||||||
|
checkStrictly: false,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: 'data-source-field-select',
|
||||||
|
name: 'field',
|
||||||
|
value: 'key',
|
||||||
|
label: '字段',
|
||||||
|
checkStrictly: false,
|
||||||
|
dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'cond-op-select',
|
||||||
|
parentFields: parentFields.value,
|
||||||
|
label: '条件',
|
||||||
|
width: 100,
|
||||||
|
name: 'op',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '值',
|
||||||
|
width: 100,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'value',
|
||||||
|
type: (mForm, { model }) => {
|
||||||
|
const [id, ...fieldNames] = model.field;
|
||||||
|
|
||||||
|
const ds = dataSourceService?.getDataSourceById(id);
|
||||||
|
|
||||||
|
let fields = ds?.fields || [];
|
||||||
|
let type = '';
|
||||||
|
(fieldNames || []).forEach((fieldName: string) => {
|
||||||
|
const field = fields.find((f) => f.name === fieldName);
|
||||||
|
fields = field?.fields || [];
|
||||||
|
type = field?.type || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type === 'number') {
|
||||||
|
return 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'boolean') {
|
||||||
|
return 'select';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'text';
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{ text: 'true', value: true },
|
||||||
|
{ text: 'false', value: false },
|
||||||
|
],
|
||||||
|
display: (vm, { model }) => !['between', 'not_between'].includes(model.op),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'range',
|
||||||
|
type: 'number-range',
|
||||||
|
display: (vm, { model }) => ['between', 'not_between'].includes(model.op),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
</script>
|
@ -21,6 +21,7 @@ import Code from './fields/Code.vue';
|
|||||||
import CodeLink from './fields/CodeLink.vue';
|
import CodeLink from './fields/CodeLink.vue';
|
||||||
import CodeSelect from './fields/CodeSelect.vue';
|
import CodeSelect from './fields/CodeSelect.vue';
|
||||||
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
||||||
|
import CondOpSelect from './fields/CondOpSelect.vue';
|
||||||
import DataSourceFields from './fields/DataSourceFields.vue';
|
import DataSourceFields from './fields/DataSourceFields.vue';
|
||||||
import DataSourceFieldSelect from './fields/DataSourceFieldSelect/Index.vue';
|
import DataSourceFieldSelect from './fields/DataSourceFieldSelect/Index.vue';
|
||||||
import DataSourceInput from './fields/DataSourceInput.vue';
|
import DataSourceInput from './fields/DataSourceInput.vue';
|
||||||
@ -28,6 +29,7 @@ import DataSourceMethods from './fields/DataSourceMethods.vue';
|
|||||||
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
|
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
|
||||||
import DataSourceMocks from './fields/DataSourceMocks.vue';
|
import DataSourceMocks from './fields/DataSourceMocks.vue';
|
||||||
import DataSourceSelect from './fields/DataSourceSelect.vue';
|
import DataSourceSelect from './fields/DataSourceSelect.vue';
|
||||||
|
import DisplayConds from './fields/DisplayConds.vue';
|
||||||
import EventSelect from './fields/EventSelect.vue';
|
import EventSelect from './fields/EventSelect.vue';
|
||||||
import KeyValue from './fields/KeyValue.vue';
|
import KeyValue from './fields/KeyValue.vue';
|
||||||
import PageFragmentSelect from './fields/PageFragmentSelect.vue';
|
import PageFragmentSelect from './fields/PageFragmentSelect.vue';
|
||||||
@ -84,6 +86,8 @@ export { default as Resizer } from './components/Resizer.vue';
|
|||||||
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
||||||
export { default as FloatingBox } from './components/FloatingBox.vue';
|
export { default as FloatingBox } from './components/FloatingBox.vue';
|
||||||
export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
|
export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
|
||||||
|
export { default as DisplayConds } from './fields/DisplayConds.vue';
|
||||||
|
export { default as CondOpSelect } from './fields/CondOpSelect.vue';
|
||||||
|
|
||||||
const defaultInstallOpt: InstallOptions = {
|
const defaultInstallOpt: InstallOptions = {
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
@ -114,5 +118,7 @@ export default {
|
|||||||
app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
|
app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
|
||||||
app.component('m-fields-data-source-field-select', DataSourceFieldSelect);
|
app.component('m-fields-data-source-field-select', DataSourceFieldSelect);
|
||||||
app.component('m-fields-page-fragment-select', PageFragmentSelect);
|
app.component('m-fields-page-fragment-select', PageFragmentSelect);
|
||||||
|
app.component('m-fields-display-conds', DisplayConds);
|
||||||
|
app.component('m-fields-cond-op-select', CondOpSelect);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -677,6 +677,11 @@ export interface DataSourceFieldSelectConfig extends FormItem {
|
|||||||
notEditable?: boolean | FilterFunction;
|
notEditable?: boolean | FilterFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CondOpSelectConfig extends FormItem {
|
||||||
|
type: 'cond-op';
|
||||||
|
parentFields?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/** 可新增的数据源类型选项 */
|
/** 可新增的数据源类型选项 */
|
||||||
export interface DatasourceTypeOption {
|
export interface DatasourceTypeOption {
|
||||||
/** 数据源类型 */
|
/** 数据源类型 */
|
||||||
|
@ -215,7 +215,10 @@ export const getCascaderOptionsFromFields = (
|
|||||||
dataSourceFieldType.push('any');
|
dataSourceFieldType.push('any');
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType);
|
let children: CascaderOption[] = [];
|
||||||
|
if (field.type && ['any', 'array', 'object'].includes(field.type)) {
|
||||||
|
children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType);
|
||||||
|
}
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
label: `${field.title || field.name}(${field.type})`,
|
label: `${field.title || field.name}(${field.type})`,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
/*
|
/*
|
||||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||||
*
|
*
|
||||||
@ -18,19 +19,17 @@
|
|||||||
|
|
||||||
import type { FormConfig, FormState, TabPaneConfig } from '@tmagic/form';
|
import type { FormConfig, FormState, TabPaneConfig } from '@tmagic/form';
|
||||||
|
|
||||||
import dataSourceService from '@editor/services/dataSource';
|
export const arrayOptions = [
|
||||||
|
|
||||||
const arrayOptions = [
|
|
||||||
{ text: '包含', value: 'include' },
|
{ text: '包含', value: 'include' },
|
||||||
{ text: '不包含', value: 'not_include' },
|
{ text: '不包含', value: 'not_include' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const eqOptions = [
|
export const eqOptions = [
|
||||||
{ text: '等于', value: '=' },
|
{ text: '等于', value: '=' },
|
||||||
{ text: '不等于', value: '!=' },
|
{ text: '不等于', value: '!=' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const numberOptions = [
|
export const numberOptions = [
|
||||||
{ text: '大于', value: '>' },
|
{ text: '大于', value: '>' },
|
||||||
{ text: '大于等于', value: '>=' },
|
{ text: '大于等于', value: '>=' },
|
||||||
{ text: '小于', value: '<' },
|
{ text: '小于', value: '<' },
|
||||||
@ -359,106 +358,10 @@ export const displayTabConfig: TabPaneConfig = {
|
|||||||
display: (vm: FormState, { model }: any) => model.type !== 'page',
|
display: (vm: FormState, { model }: any) => model.type !== 'page',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'groupList',
|
type: 'display-conds',
|
||||||
name: 'displayConds',
|
name: 'displayConds',
|
||||||
titlePrefix: '条件组',
|
titlePrefix: '条件组',
|
||||||
expandAll: true,
|
defaultValue: [],
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'table',
|
|
||||||
name: 'cond',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'data-source-field-select',
|
|
||||||
name: 'field',
|
|
||||||
value: 'key',
|
|
||||||
label: '字段',
|
|
||||||
checkStrictly: false,
|
|
||||||
dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'select',
|
|
||||||
options: (mForm, { model }) => {
|
|
||||||
const [id, ...fieldNames] = model.field;
|
|
||||||
|
|
||||||
const ds = dataSourceService.getDataSourceById(id);
|
|
||||||
|
|
||||||
let fields = ds?.fields || [];
|
|
||||||
let type = '';
|
|
||||||
(fieldNames || []).forEach((fieldName: string) => {
|
|
||||||
const field = fields.find((f) => f.name === fieldName);
|
|
||||||
fields = field?.fields || [];
|
|
||||||
type = 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',
|
|
||||||
type: (mForm, { model }) => {
|
|
||||||
const [id, ...fieldNames] = model.field;
|
|
||||||
|
|
||||||
const ds = dataSourceService.getDataSourceById(id);
|
|
||||||
|
|
||||||
let fields = ds?.fields || [];
|
|
||||||
let type = '';
|
|
||||||
(fieldNames || []).forEach((fieldName: string) => {
|
|
||||||
const field = fields.find((f) => f.name === fieldName);
|
|
||||||
fields = field?.fields || [];
|
|
||||||
type = field?.type || '';
|
|
||||||
});
|
|
||||||
|
|
||||||
if (type === 'number') {
|
|
||||||
return 'number';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'boolean') {
|
|
||||||
return 'select';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'text';
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{ text: 'true', value: true },
|
|
||||||
{ text: 'false', value: false },
|
|
||||||
],
|
|
||||||
display: (vm, { model }) => !['between', 'not_between'].includes(model.op),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'range',
|
|
||||||
type: 'number-range',
|
|
||||||
display: (vm, { model }) => ['between', 'not_between'].includes(model.op),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,7 @@ const options = ref<CascaderOption[]>([]);
|
|||||||
const remoteData = ref<any>(null);
|
const remoteData = ref<any>(null);
|
||||||
|
|
||||||
const checkStrictly = computed(() => filterFunction(mForm, props.config.checkStrictly, props));
|
const checkStrictly = computed(() => filterFunction(mForm, props.config.checkStrictly, props));
|
||||||
const valueSeparator = computed(() => filterFunction(mForm, props.config.valueSeparator, props));
|
const valueSeparator = computed(() => filterFunction<string>(mForm, props.config.valueSeparator, props));
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -11,7 +11,7 @@ import { computed, inject } from 'vue';
|
|||||||
|
|
||||||
import { TMagicCheckbox, TMagicCheckboxGroup } from '@tmagic/design';
|
import { TMagicCheckbox, TMagicCheckboxGroup } from '@tmagic/design';
|
||||||
|
|
||||||
import type { CheckboxGroupConfig, FieldProps, FormState } from '../schema';
|
import type { CheckboxGroupConfig, CheckboxGroupOption, FieldProps, FormState } from '../schema';
|
||||||
import { filterFunction } from '../utils/form';
|
import { filterFunction } from '../utils/form';
|
||||||
import { useAddField } from '../utils/useAddField';
|
import { useAddField } from '../utils/useAddField';
|
||||||
|
|
||||||
@ -37,7 +37,8 @@ const changeHandler = (v: Array<string | number | boolean>) => {
|
|||||||
const mForm = inject<FormState | undefined>('mForm');
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
const options = computed(() => {
|
const options = computed(() => {
|
||||||
if (Array.isArray(props.config.options)) return props.config.options;
|
if (Array.isArray(props.config.options)) return props.config.options;
|
||||||
if (typeof props.config.options === 'function') return filterFunction(mForm, props.config.options, props);
|
if (typeof props.config.options === 'function')
|
||||||
|
return filterFunction<CheckboxGroupOption[]>(mForm, props.config.options, props) || [];
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -170,6 +170,7 @@ export type FilterFunction<T = boolean> = (
|
|||||||
formValue: Record<any, any>;
|
formValue: Record<any, any>;
|
||||||
prop: string;
|
prop: string;
|
||||||
config: any;
|
config: any;
|
||||||
|
index?: number;
|
||||||
},
|
},
|
||||||
) => T;
|
) => T;
|
||||||
|
|
||||||
@ -433,18 +434,18 @@ export interface ColorPickConfig extends FormItem {
|
|||||||
type: 'colorPicker';
|
type: 'colorPicker';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CheckboxGroupOption {
|
||||||
|
value: any;
|
||||||
|
text: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多选框组
|
* 多选框组
|
||||||
*/
|
*/
|
||||||
export interface CheckboxGroupConfig extends FormItem {
|
export interface CheckboxGroupConfig extends FormItem {
|
||||||
type: 'checkbox-group';
|
type: 'checkbox-group';
|
||||||
options:
|
options: CheckboxGroupOption[] | FilterFunction<CheckboxGroupOption[]>;
|
||||||
| {
|
|
||||||
value: any;
|
|
||||||
text: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
}[]
|
|
||||||
| Function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
ChildConfig,
|
ChildConfig,
|
||||||
ContainerCommonConfig,
|
ContainerCommonConfig,
|
||||||
DaterangeConfig,
|
DaterangeConfig,
|
||||||
|
FilterFunction,
|
||||||
FormConfig,
|
FormConfig,
|
||||||
FormState,
|
FormState,
|
||||||
FormValue,
|
FormValue,
|
||||||
@ -181,20 +182,24 @@ const getDefaultValue = function (mForm: FormState | undefined, { defaultValue,
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterFunction = <T = any>(mForm: FormState | undefined, config: T, props: any) => {
|
export const filterFunction = <T = any>(
|
||||||
if (typeof config !== 'function') {
|
mForm: FormState | undefined,
|
||||||
return config;
|
config: T | FilterFunction<T> | undefined,
|
||||||
|
props: any,
|
||||||
|
) => {
|
||||||
|
if (typeof config === 'function') {
|
||||||
|
return (config as FilterFunction<T>)(mForm, {
|
||||||
|
values: mForm?.initValues || {},
|
||||||
|
model: props.model,
|
||||||
|
parent: mForm?.parentValues || {},
|
||||||
|
formValue: mForm?.values || props.model,
|
||||||
|
prop: props.prop,
|
||||||
|
config: props.config,
|
||||||
|
index: props.index,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return config(mForm, {
|
return config;
|
||||||
values: mForm?.initValues || {},
|
|
||||||
model: props.model,
|
|
||||||
parent: mForm?.parentValues || {},
|
|
||||||
formValue: mForm?.values || props.model,
|
|
||||||
prop: props.prop,
|
|
||||||
config: props.config,
|
|
||||||
index: props.index,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const display = function (mForm: FormState | undefined, config: any, props: any) {
|
export const display = function (mForm: FormState | undefined, config: any, props: any) {
|
||||||
|
@ -139,6 +139,7 @@ export interface MComponent {
|
|||||||
style?: {
|
style?: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
displayConds?: DisplayCond[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,3 +286,14 @@ export type HookData = {
|
|||||||
/** 参数 */
|
/** 参数 */
|
||||||
params?: object;
|
params?: object;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface DisplayCondItem {
|
||||||
|
field: string[];
|
||||||
|
op: string;
|
||||||
|
value?: any;
|
||||||
|
range?: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DisplayCond {
|
||||||
|
cond: DisplayCondItem[];
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { type MContainer, NodeType } from '@tmagic/schema';
|
import { type DisplayCond, type MContainer, NodeType } from '@tmagic/schema';
|
||||||
import { useApp } from '@tmagic/vue-runtime-help';
|
import { useApp } from '@tmagic/vue-runtime-help';
|
||||||
|
|
||||||
import Container from '../../container';
|
import Container from '../../container';
|
||||||
@ -20,6 +20,7 @@ const props = withDefaults(
|
|||||||
dsField: string[];
|
dsField: string[];
|
||||||
itemConfig: {
|
itemConfig: {
|
||||||
layout: string;
|
layout: string;
|
||||||
|
displayConds: DisplayCond[];
|
||||||
style: Record<string, string | number>;
|
style: Record<string, string | number>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -46,18 +47,26 @@ const configs = computed(() => {
|
|||||||
iteratorData.push({});
|
iteratorData.push({});
|
||||||
}
|
}
|
||||||
|
|
||||||
return iteratorData.map((itemData) => ({
|
return iteratorData.map((itemData) => {
|
||||||
items:
|
const condResult =
|
||||||
app?.dataSourceManager?.compliedIteratorItems(itemData, props.config.items, props.config.dsField) ??
|
app?.platform !== 'editor'
|
||||||
props.config.items,
|
? app?.dataSourceManager?.compliedIteratorItemConds(itemData, props.config.itemConfig.displayConds) ?? true
|
||||||
id: '',
|
: true;
|
||||||
type: NodeType.CONTAINER,
|
|
||||||
style: {
|
return {
|
||||||
...props.config.itemConfig.style,
|
items:
|
||||||
position: 'relative',
|
app?.dataSourceManager?.compliedIteratorItems(itemData, props.config.items, props.config.dsField) ??
|
||||||
left: 0,
|
props.config.items,
|
||||||
top: 0,
|
id: '',
|
||||||
},
|
type: NodeType.CONTAINER,
|
||||||
}));
|
condResult,
|
||||||
|
style: {
|
||||||
|
...props.config.itemConfig.style,
|
||||||
|
position: 'relative',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -43,6 +43,13 @@ export default [
|
|||||||
title: '子项配置',
|
title: '子项配置',
|
||||||
name: 'itemConfig',
|
name: 'itemConfig',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
type: 'display-conds',
|
||||||
|
name: 'displayConds',
|
||||||
|
titlePrefix: '条件组',
|
||||||
|
parentFields: (formState: any, { formValue }: any) => formValue.dsField,
|
||||||
|
defaultValue: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'layout',
|
name: 'layout',
|
||||||
text: '容器布局',
|
text: '容器布局',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user