mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-02 04:49:48 +08:00
feat(editor): 样式配置支持配置数据源字段
This commit is contained in:
parent
900acb6eb0
commit
cda6c0f791
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/** 可新增的数据源类型选项 */
|
||||
|
@ -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',
|
||||
|
@ -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) => {
|
||||
|
@ -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) => {
|
||||
|
@ -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) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user