feat(editor): 样式配置支持配置数据源字段

This commit is contained in:
roymondchen 2024-03-08 20:04:48 +08:00
parent 900acb6eb0
commit cda6c0f791
6 changed files with 258 additions and 64 deletions

View File

@ -1,22 +1,37 @@
<template>
<MCascader
:config="cascaderConfig"
:model="model"
:name="name"
:disabled="disabled"
:size="size"
:last-values="lastValues"
:init-values="initValues"
:values="values"
:prop="`${prop}${prop ? '.' : ''}${name}`"
@change="onChangeHandler"
></MCascader>
<div style="width: 100%; display: flex; align-items: center">
<component
style="width: 100%"
:is="tagName"
:config="showDataSourceFieldSelect || !config.fieldConfig ? cascaderConfig : config.fieldConfig"
:model="model"
:name="name"
:disabled="disabled"
:size="size"
:last-values="lastValues"
:init-values="initValues"
:values="values"
:prop="`${prop}${prop ? '.' : ''}${name}`"
@change="onChangeHandler"
></component>
<TMagicButton
v-if="config.fieldConfig"
style="margin-left: 5px"
link
:type="showDataSourceFieldSelect ? 'primary' : 'default'"
:icon="Coin"
:size="size"
@click="showDataSourceFieldSelect = !showDataSourceFieldSelect"
></TMagicButton>
</div>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { computed, inject, onMounted, ref, resolveComponent } from 'vue';
import { Coin } from '@element-plus/icons-vue';
import type { CascaderConfig, CascaderOption, FieldProps } from '@tmagic/form';
import { TMagicButton } from '@tmagic/design';
import type { CascaderConfig, CascaderOption, FieldProps, FormState } from '@tmagic/form';
import { MCascader } from '@tmagic/form';
import type { DataSchema, DataSourceFieldType } from '@tmagic/schema';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
@ -36,14 +51,42 @@ const props = withDefaults(defineProps<FieldProps<DataSourceFieldSelectConfig>>(
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
const getOptionChildren = (fields: DataSchema[] = [], fieldType: DataSourceFieldType[] = []): CascaderOption[] =>
fields
.filter((field) => !fieldType.length || fieldType.includes(field.type || 'string') || field.type === 'object')
.map((field) => ({
const getOptionChildren = (
fields: DataSchema[] = [],
dataSourceFieldType: DataSourceFieldType[] = ['any'],
): CascaderOption[] => {
const child: CascaderOption[] = [];
fields.forEach((field) => {
if (!dataSourceFieldType.length) {
dataSourceFieldType.push('any');
}
const children = getOptionChildren(field.fields, dataSourceFieldType);
const item = {
label: field.title || field.name,
value: field.name,
children: field.type === 'array' ? [] : getOptionChildren(field.fields, fieldType),
}));
children,
};
const fieldType = field.type || 'any';
if (dataSourceFieldType.includes('any') || dataSourceFieldType.includes(fieldType)) {
child.push(item);
return;
}
if (!dataSourceFieldType.includes(fieldType) && fieldType !== 'object') {
return;
}
if (!children.length && ['object', 'array', 'any'].includes(field.type || '')) {
return;
}
child.push(item);
});
return child;
};
const cascaderConfig = computed<CascaderConfig>(() => {
const valueIsKey = props.config.value === 'key';
@ -57,13 +100,50 @@ const cascaderConfig = computed<CascaderConfig>(() => {
dataSources.value?.map((ds) => ({
label: ds.title || ds.id,
value: valueIsKey ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
children: getOptionChildren(ds.fields, props.config.fieldType),
children: getOptionChildren(ds.fields, props.config.dataSourceFieldType),
})) || [];
return options.filter((option) => option.children.length);
},
};
});
const showDataSourceFieldSelect = ref(false);
onMounted(() => {
const value = props.model[props.name];
if (
Array.isArray(value) &&
typeof value[0] === 'string' &&
value[0].startsWith(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX)
) {
return (showDataSourceFieldSelect.value = true);
}
});
const mForm = inject<FormState | undefined>('mForm');
const type = computed((): string => {
let type = props.config.fieldConfig?.type;
if (typeof type === 'function') {
type = type(mForm, {
model: props.model,
});
}
if (type === 'form') return '';
if (type === 'container') return '';
return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (props.config.items ? '' : 'text');
});
const tagName = computed(() => {
if (showDataSourceFieldSelect.value || !props.config.fieldConfig) {
return MCascader;
}
const component = resolveComponent(`m-${props.config.items ? 'form' : 'fields'}-${type.value}`);
if (typeof component !== 'string') return component;
return 'm-fields-text';
});
const onChangeHandler = (value: any) => {
emit('change', value);
};

View File

@ -19,7 +19,7 @@
import type { Component } from 'vue';
import type { PascalCasedProperties } from 'type-fest';
import type { ColumnConfig, FilterFunction, FormConfig, FormItem, Input } from '@tmagic/form';
import type { ChildConfig, ColumnConfig, FilterFunction, FormConfig, FormItem, Input } from '@tmagic/form';
import type {
CodeBlockContent,
CodeBlockDSL,
@ -642,7 +642,8 @@ export interface DataSourceFieldSelectConfig extends FormItem {
value?: 'key' | 'value';
/** 是否严格的遵守父子节点不互相关联 */
checkStrictly?: boolean;
fieldType?: DataSourceFieldType[];
dataSourceFieldType?: DataSourceFieldType[];
fieldConfig?: ChildConfig;
}
/** 可新增的数据源类型选项 */

View File

@ -50,30 +50,59 @@ export const styleTabConfig: TabPaneConfig = {
legend: '位置',
items: [
{
type: 'data-source-field-select',
name: 'position',
type: 'checkbox',
activeValue: 'fixed',
inactiveValue: 'absolute',
defaultValue: 'absolute',
text: '固定定位',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'checkbox',
activeValue: 'fixed',
inactiveValue: 'absolute',
defaultValue: 'absolute',
},
},
{
type: 'data-source-field-select',
name: 'left',
text: 'left',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'top',
text: 'top',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedBottom',
},
{
type: 'data-source-field-select',
name: 'right',
text: 'right',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'bottom',
text: 'bottom',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
disabled: (vm: FormState, { model }: any) =>
model.position === 'fixed' && model._magic_position === 'fixedTop',
},
@ -84,25 +113,42 @@ export const styleTabConfig: TabPaneConfig = {
legend: '盒子',
items: [
{
type: 'data-source-field-select',
name: 'width',
text: '宽度',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'height',
text: '高度',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
text: 'overflow',
name: 'overflow',
type: 'select',
options: [
{ text: 'visible', value: 'visible' },
{ text: 'hidden', value: 'hidden' },
{ text: 'clip', value: 'clip' },
{ text: 'scroll', value: 'scroll' },
{ text: 'auto', value: 'auto' },
{ text: 'overlay', value: 'overlay' },
],
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'select',
options: [
{ text: 'visible', value: 'visible' },
{ text: 'hidden', value: 'hidden' },
{ text: 'clip', value: 'clip' },
{ text: 'scroll', value: 'scroll' },
{ text: 'auto', value: 'auto' },
{ text: 'overlay', value: 'overlay' },
],
},
},
],
},
@ -111,32 +157,48 @@ export const styleTabConfig: TabPaneConfig = {
legend: '边框',
items: [
{
type: 'data-source-field-select',
name: 'borderWidth',
text: '宽度',
defaultValue: '0',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'borderColor',
text: '颜色',
type: 'colorPicker',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
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' },
],
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'select',
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' },
],
},
},
],
},
@ -145,31 +207,53 @@ export const styleTabConfig: TabPaneConfig = {
legend: '背景',
items: [
{
type: 'data-source-field-select',
name: 'backgroundImage',
text: '背景图',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'backgroundColor',
text: '背景颜色',
type: 'colorPicker',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'colorPicker',
},
},
{
type: 'data-source-field-select',
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' },
],
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'select',
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' },
],
},
},
{
type: 'data-source-field-select',
name: 'backgroundSize',
text: '背景图大小',
defaultValue: '100% 100%',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'text',
},
},
],
},
@ -178,17 +262,34 @@ export const styleTabConfig: TabPaneConfig = {
legend: '字体',
items: [
{
type: 'data-source-field-select',
name: 'color',
text: '颜色',
type: 'colorPicker',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'colorPicker',
},
},
{
type: 'data-source-field-select',
name: 'fontSize',
text: '大小',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'fontWeight',
text: '粗细',
checkStrictly: false,
dataSourceFieldType: ['string', 'number'],
fieldConfig: {
type: 'text',
},
},
],
},
@ -198,12 +299,24 @@ export const styleTabConfig: TabPaneConfig = {
name: 'transform',
items: [
{
type: 'data-source-field-select',
name: 'rotate',
text: '旋转角度',
checkStrictly: false,
dataSourceFieldType: ['string'],
fieldConfig: {
type: 'text',
},
},
{
type: 'data-source-field-select',
name: 'scale',
text: '缩放',
checkStrictly: false,
dataSourceFieldType: ['number', 'string'],
fieldConfig: {
type: 'text',
},
},
],
},
@ -261,7 +374,7 @@ export const displayTabConfig: TabPaneConfig = {
value: 'key',
label: '字段',
checkStrictly: false,
fieldType: ['string', 'number', 'boolean', 'any'],
dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
},
{
type: 'select',

View File

@ -22,7 +22,7 @@ export default [
name: 'iteratorData',
text: '数据源数据',
value: 'value',
fieldType: ['array'],
dataSourceFieldType: ['array'],
checkStrictly: false,
type: 'data-source-field-select',
onChange: (vm: any, v: string[] = [], { model }: any) => {

View File

@ -22,7 +22,7 @@ export default [
name: 'iteratorData',
text: '数据源数据',
value: 'value',
fieldType: ['array'],
dataSourceFieldType: ['array'],
checkStrictly: false,
type: 'data-source-field-select',
onChange: (vm: any, v: string[] = [], { model }: any) => {

View File

@ -22,7 +22,7 @@ export default [
name: 'iteratorData',
text: '数据源数据',
value: 'value',
fieldType: ['array'],
dataSourceFieldType: ['array'],
checkStrictly: false,
type: 'data-source-field-select',
onChange: (vm: any, v: string[] = [], { model }: any) => {