mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-05 19:41:40 +08:00
feat: 支持迭代器容器
This commit is contained in:
parent
6dbac52c50
commit
e692e01c4f
@ -21,10 +21,16 @@ import EventEmitter from 'events';
|
||||
import { cloneDeep, template } from 'lodash-es';
|
||||
|
||||
import type { AppCore, DataSourceSchema, Id, MNode } from '@tmagic/schema';
|
||||
import { compiledCond, compiledNode, DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, isObject } from '@tmagic/utils';
|
||||
import {
|
||||
compiledNode,
|
||||
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
|
||||
DSL_NODE_KEY_COPY_PREFIX,
|
||||
getValueByKeyPath,
|
||||
} from '@tmagic/utils';
|
||||
|
||||
import { DataSource, HttpDataSource } from './data-sources';
|
||||
import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions } from './types';
|
||||
import { compliedConditions, createIteratorContentData } from './utils';
|
||||
|
||||
class DataSourceManager extends EventEmitter {
|
||||
private static dataSourceClassMap = new Map<string, typeof DataSource>();
|
||||
@ -189,15 +195,7 @@ class DataSourceManager extends EventEmitter {
|
||||
|
||||
if (!data) return value;
|
||||
|
||||
return fields.reduce((accumulator, currentValue: any) => {
|
||||
if (Array.isArray(accumulator)) return accumulator;
|
||||
|
||||
if (isObject(accumulator)) {
|
||||
return accumulator[currentValue];
|
||||
}
|
||||
|
||||
return '';
|
||||
}, data);
|
||||
return getValueByKeyPath(fields.join('.'), data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,34 +208,39 @@ class DataSourceManager extends EventEmitter {
|
||||
}
|
||||
|
||||
public compliedConds(node: MNode) {
|
||||
if (!node.displayConds || !Array.isArray(node.displayConds) || !node.displayConds.length) return true;
|
||||
return compliedConditions(node, this.data);
|
||||
}
|
||||
|
||||
for (const { cond } of node.displayConds) {
|
||||
if (!cond) continue;
|
||||
public compliedIteratorItems(itemData: any, items: MNode[], dataSourceField: string[] = []) {
|
||||
return items.map((item) => {
|
||||
const keys: string[] = [];
|
||||
const [dsId, ...fields] = dataSourceField;
|
||||
|
||||
let result = true;
|
||||
for (const { op, value, range, field } of cond) {
|
||||
const [sourceId, fieldKey] = field;
|
||||
|
||||
const dsData = this.data[sourceId];
|
||||
|
||||
if (!dsData || !fieldKey) {
|
||||
break;
|
||||
Object.entries(item).forEach(([key, value]) => {
|
||||
if (
|
||||
typeof value === 'string' &&
|
||||
!key.startsWith(DSL_NODE_KEY_COPY_PREFIX) &&
|
||||
value.includes(`${dsId}`) &&
|
||||
/\$\{([\s\S]+?)\}/.test(value)
|
||||
) {
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
const fieldValue = dsData[fieldKey];
|
||||
if (!compiledCond(op, fieldValue, value, range)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return compiledNode(
|
||||
(value: string) => template(value)(createIteratorContentData(itemData, dsId, fields)),
|
||||
cloneDeep(item),
|
||||
{
|
||||
[dsId]: {
|
||||
[item.id]: {
|
||||
name: '',
|
||||
keys,
|
||||
},
|
||||
},
|
||||
},
|
||||
dsId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
@ -17,11 +17,12 @@
|
||||
*/
|
||||
import { cloneDeep, union } from 'lodash-es';
|
||||
|
||||
import type { AppCore, MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||
import { getDepNodeIds, getNodes, isPage, isPageFragment, replaceChildNode } from '@tmagic/utils';
|
||||
import type { AppCore } from '@tmagic/schema';
|
||||
import { getDepNodeIds, getNodes } from '@tmagic/utils';
|
||||
|
||||
import DataSourceManager from './DataSourceManager';
|
||||
import type { ChangeEvent, DataSourceManagerData } from './types';
|
||||
import { updateNode } from './utils';
|
||||
|
||||
/**
|
||||
* 创建数据源管理器
|
||||
@ -73,12 +74,3 @@ export const createDataSourceManager = (app: AppCore, useMock?: boolean, initial
|
||||
|
||||
return dataSourceManager;
|
||||
};
|
||||
|
||||
const updateNode = (node: MNode, dsl: MApp) => {
|
||||
if (isPage(node) || isPageFragment(node)) {
|
||||
const index = dsl.items?.findIndex((child: MNode) => child.id === node.id);
|
||||
dsl.items.splice(index, 1, node as MPage | MPageFragment);
|
||||
} else {
|
||||
replaceChildNode(node, dsl!.items);
|
||||
}
|
||||
};
|
||||
|
@ -19,4 +19,5 @@
|
||||
export { default as DataSourceManager } from './DataSourceManager';
|
||||
export * from './data-sources';
|
||||
export * from './createDataSourceManager';
|
||||
export * from './utils';
|
||||
export * from './types';
|
||||
|
59
packages/data-source/src/utils.ts
Normal file
59
packages/data-source/src/utils.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import type { MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||||
import { compiledCond, getValueByKeyPath, isPage, isPageFragment, replaceChildNode } from '@tmagic/utils';
|
||||
|
||||
import type { DataSourceManagerData } from './types';
|
||||
|
||||
export const compliedConditions = (node: MNode, data: DataSourceManagerData) => {
|
||||
if (!node.displayConds || !Array.isArray(node.displayConds) || !node.displayConds.length) return true;
|
||||
|
||||
for (const { cond } of node.displayConds) {
|
||||
if (!cond) continue;
|
||||
|
||||
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('.'), data[sourceId]);
|
||||
|
||||
if (!compiledCond(op, fieldValue, value, range)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const updateNode = (node: MNode, dsl: MApp) => {
|
||||
if (isPage(node) || isPageFragment(node)) {
|
||||
const index = dsl.items?.findIndex((child: MNode) => child.id === node.id);
|
||||
dsl.items.splice(index, 1, node as MPage | MPageFragment);
|
||||
} else {
|
||||
replaceChildNode(node, dsl!.items);
|
||||
}
|
||||
};
|
||||
|
||||
export const createIteratorContentData = (itemData: any, dsId: string, fields: string[] = []) => {
|
||||
const data = {
|
||||
[dsId]: {},
|
||||
};
|
||||
|
||||
fields.reduce((obj: any, field, index) => {
|
||||
obj[field] = index === fields.length - 1 ? itemData : {};
|
||||
|
||||
return obj[field];
|
||||
}, data[dsId]);
|
||||
|
||||
return data;
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
type CodeBlockContent,
|
||||
type DataSchema,
|
||||
type DataSourceSchema,
|
||||
type DepData,
|
||||
type HookData,
|
||||
@ -46,14 +47,55 @@ export const createDataSourceTarget = (ds: DataSourceSchema, initialDeps: DepDat
|
||||
isTarget: (key: string | number, value: any) => {
|
||||
// 关联数据源对象,如:{ isBindDataSource: true, dataSourceId: 'xxx'}
|
||||
// 使用data-source-select value: 'value' 可以配置出来
|
||||
// 或者在模板在使用数据源,如:`xxx${id.field}xxx`
|
||||
if (
|
||||
(value?.isBindDataSource && value.dataSourceId && value.dataSourceId === ds.id) ||
|
||||
(typeof value === 'string' && value.includes(`${ds.id}`) && /\$\{([\s\S]+?)\}/.test(value))
|
||||
) {
|
||||
|
||||
if (value?.isBindDataSource && value.dataSourceId && value.dataSourceId === ds.id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 或者在模板在使用数据源,如:`xxx${dsId.field}xxx${dsId.field}`
|
||||
if (typeof value === 'string' && value.includes(`${ds.id}`) && /\$\{([\s\S]+?)\}/.test(value)) {
|
||||
// 模板中可能会存在多个表达式,将表达式从模板中提取出来
|
||||
const templates = value.match(/\$\{([\s\S]+?)\}/g) || [];
|
||||
|
||||
for (const tpl of templates) {
|
||||
const keys = tpl
|
||||
// 将${dsId.xxxx} 转成 dsId.xxxx
|
||||
.substring(2, tpl.length - 1)
|
||||
// 将 array[0] 转成 array.0
|
||||
.replaceAll(/\[(\d+)\]/g, '.$1')
|
||||
.split('.');
|
||||
const dsId = keys.shift();
|
||||
|
||||
if (!dsId || dsId !== ds.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ${dsId.array} ${dsId.array[0]} ${dsId.array[0].a} 这种是依赖
|
||||
// ${dsId.array.a} 这种不是依赖,这种需要再迭代器容器中的组件才能使用,依赖由迭代器处理
|
||||
let includeArray = false;
|
||||
keys.reduce((accumulator: DataSchema[], currentValue: string, currentIndex: number) => {
|
||||
const field = accumulator.find(({ name }) => name === currentValue);
|
||||
if (
|
||||
field &&
|
||||
field.type === 'array' &&
|
||||
typeof keys[currentIndex + 1] !== 'number' &&
|
||||
currentIndex < keys.length - 1
|
||||
) {
|
||||
includeArray = true;
|
||||
}
|
||||
return field?.fields || [];
|
||||
}, ds.fields);
|
||||
|
||||
if (includeArray) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}
|
||||
if (
|
||||
value?.isBindDataSourceField &&
|
||||
|
@ -1,23 +1,32 @@
|
||||
<template>
|
||||
<m-form-container
|
||||
:config="{
|
||||
...config,
|
||||
...cascaderConfig,
|
||||
}"
|
||||
<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"
|
||||
></m-form-container>
|
||||
></MCascader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, inject } from 'vue';
|
||||
|
||||
import type { CascaderOption, FieldProps } from '@tmagic/form';
|
||||
import type { DataSchema } from '@tmagic/schema';
|
||||
import type { CascaderConfig, CascaderOption, FieldProps } 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';
|
||||
|
||||
import type { DataSourceFieldSelectConfig, Services } from '@editor/type';
|
||||
|
||||
defineOptions({
|
||||
name: 'MEditorDataSourceFieldSelect',
|
||||
});
|
||||
|
||||
const services = inject<Services>('services');
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
@ -27,29 +36,31 @@ const props = withDefaults(defineProps<FieldProps<DataSourceFieldSelectConfig>>(
|
||||
|
||||
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
|
||||
|
||||
const getOptionChildren = (fields: DataSchema[] = []): CascaderOption[] =>
|
||||
fields.map((field) => ({
|
||||
label: field.title || field.name,
|
||||
value: field.name,
|
||||
children: field.type === 'array' ? [] : getOptionChildren(field.fields),
|
||||
}));
|
||||
const getOptionChildren = (fields: DataSchema[] = [], fieldType: DataSourceFieldType[] = []): CascaderOption[] =>
|
||||
fields
|
||||
.filter((field) => !fieldType.length || fieldType.includes(field.type || 'string') || field.type === 'object')
|
||||
.map((field) => ({
|
||||
label: field.title || field.name,
|
||||
value: field.name,
|
||||
children: field.type === 'array' ? [] : getOptionChildren(field.fields, fieldType),
|
||||
}));
|
||||
|
||||
const cascaderConfig = computed(() => {
|
||||
const cascaderConfig = computed<CascaderConfig>(() => {
|
||||
const valueIsKey = props.config.value === 'key';
|
||||
|
||||
return {
|
||||
type: 'cascader',
|
||||
name: props.name,
|
||||
checkStrictly: !valueIsKey,
|
||||
text: '',
|
||||
options: () =>
|
||||
dataSources.value
|
||||
?.filter((ds) => ds.fields?.length)
|
||||
?.map((ds) => ({
|
||||
checkStrictly: props.config.checkStrictly ?? !valueIsKey,
|
||||
popperClass: 'm-editor-data-source-field-select-popper',
|
||||
options: () => {
|
||||
const options =
|
||||
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),
|
||||
})) || [],
|
||||
children: getOptionChildren(ds.fields, props.config.fieldType),
|
||||
})) || [];
|
||||
return options.filter((option) => option.children.length);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -37,7 +37,7 @@ import { computed, inject, ref } from 'vue';
|
||||
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
||||
import { type FieldProps, type FormConfig, type FormState, MFormDrawer } from '@tmagic/form';
|
||||
import type { DataSchema } from '@tmagic/schema';
|
||||
import { MagicTable } from '@tmagic/table';
|
||||
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
||||
import { getDefaultValueFromFields } from '@tmagic/utils';
|
||||
|
||||
import type { Services } from '@editor/type';
|
||||
@ -86,7 +86,7 @@ const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
||||
emit('change', props.model[props.name]);
|
||||
};
|
||||
|
||||
const fieldColumns = [
|
||||
const fieldColumns: ColumnConfig[] = [
|
||||
{
|
||||
label: '属性名称',
|
||||
prop: 'title',
|
||||
@ -102,6 +102,13 @@ const fieldColumns = [
|
||||
{
|
||||
label: '默认值',
|
||||
prop: 'defaultValue',
|
||||
formatter(item, row) {
|
||||
try {
|
||||
return JSON.stringify(row.defaultValue);
|
||||
} catch (e) {
|
||||
return row.defaultValue;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
|
@ -24,7 +24,7 @@
|
||||
import { TMagicButton } from '@tmagic/design';
|
||||
import type { FieldProps } from '@tmagic/form';
|
||||
import type { CodeBlockContent } from '@tmagic/schema';
|
||||
import { MagicTable } from '@tmagic/table';
|
||||
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
||||
|
||||
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||
import { useDataSourceMethod } from '@editor/hooks/use-data-source-method';
|
||||
@ -49,7 +49,7 @@ const emit = defineEmits(['change']);
|
||||
|
||||
const { codeConfig, codeBlockEditor, createCode, editCode, deleteCode, submitCode } = useDataSourceMethod();
|
||||
|
||||
const methodColumns = [
|
||||
const methodColumns: ColumnConfig[] = [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'name',
|
||||
|
@ -26,7 +26,7 @@ import { computed, inject, ref } from 'vue';
|
||||
import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
|
||||
import { type FieldProps, type FormConfig, type FormState, MFormDrawer } from '@tmagic/form';
|
||||
import type { MockSchema } from '@tmagic/schema';
|
||||
import { MagicTable } from '@tmagic/table';
|
||||
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
||||
import { getDefaultValueFromFields } from '@tmagic/utils';
|
||||
|
||||
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
||||
@ -117,7 +117,7 @@ const formConfig: FormConfig = [
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
const columns: ColumnConfig[] = [
|
||||
{
|
||||
type: 'expand',
|
||||
component: CodeEditor,
|
||||
|
@ -640,7 +640,9 @@ export interface DataSourceFieldSelectConfig extends FormItem {
|
||||
* value: 要编译(数据源data[`${filed}`])
|
||||
* */
|
||||
value?: 'key' | 'value';
|
||||
fieldType?: DataSourceFieldType;
|
||||
/** 是否严格的遵守父子节点不互相关联 */
|
||||
checkStrictly?: boolean;
|
||||
fieldType?: DataSourceFieldType[];
|
||||
}
|
||||
|
||||
/** 可新增的数据源类型选项 */
|
||||
|
@ -91,6 +91,19 @@ export const styleTabConfig: TabPaneConfig = {
|
||||
name: 'height',
|
||||
text: '高度',
|
||||
},
|
||||
{
|
||||
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' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -247,15 +260,23 @@ export const displayTabConfig: TabPaneConfig = {
|
||||
name: 'field',
|
||||
value: 'key',
|
||||
label: '字段',
|
||||
checkStrictly: false,
|
||||
fieldType: ['string', 'number', 'boolean', 'any'],
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
options: (mForm, { model }) => {
|
||||
const [id, field] = model.field;
|
||||
const [id, ...fieldNames] = model.field;
|
||||
|
||||
const ds = dataSourceService.getDataSourceById(id);
|
||||
|
||||
const type = ds?.fields.find((f) => f.name === field)?.type;
|
||||
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;
|
||||
@ -287,11 +308,17 @@ export const displayTabConfig: TabPaneConfig = {
|
||||
{
|
||||
name: 'value',
|
||||
type: (mForm, { model }) => {
|
||||
const [id, field] = model.field;
|
||||
const [id, ...fieldNames] = model.field;
|
||||
|
||||
const ds = dataSourceService.getDataSourceById(id);
|
||||
|
||||
const type = ds?.fields.find((f) => f.name === field)?.type;
|
||||
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';
|
||||
|
@ -72,6 +72,7 @@ import ActionsColumn from './ActionsColumn.vue';
|
||||
import ComponentColumn from './ComponentColumn.vue';
|
||||
import ExpandColumn from './ExpandColumn.vue';
|
||||
import PopoverColumn from './PopoverColumn.vue';
|
||||
import type { ColumnConfig } from './schema';
|
||||
import TextColumn from './TextColumn.vue';
|
||||
|
||||
defineOptions({
|
||||
@ -81,7 +82,7 @@ defineOptions({
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data: any[];
|
||||
columns?: any[];
|
||||
columns?: ColumnConfig[];
|
||||
/** 合并行或列的计算方法 */
|
||||
spanMethod?: (data: { row: any; column: any; rowIndex: number; columnIndex: number }) => [number, number];
|
||||
loading?: boolean;
|
||||
|
@ -21,6 +21,8 @@ import { App } from 'vue';
|
||||
import Table from './Table.vue';
|
||||
|
||||
export { default as MagicTable } from './Table.vue';
|
||||
export * from './schema';
|
||||
export * from './utils';
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
|
@ -20,10 +20,10 @@ import { FormConfig, FormValue } from '@tmagic/form';
|
||||
|
||||
export interface ColumnActionConfig {
|
||||
type?: 'delete' | 'copy' | 'edit';
|
||||
buttonType: string;
|
||||
buttonType?: string;
|
||||
display?: (row: any) => boolean;
|
||||
text: string | ((row: any) => string);
|
||||
name: string;
|
||||
text?: string | ((row: any) => string);
|
||||
name?: string;
|
||||
tooltip?: string;
|
||||
tooltipPlacement?: string;
|
||||
icon?: any;
|
||||
|
@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@tmagic/core": "1.3.16",
|
||||
"@tmagic/schema": "1.3.16",
|
||||
"@tmagic/utils": "1.3.16",
|
||||
"qrcode": "^1.5.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0"
|
||||
|
@ -19,6 +19,7 @@
|
||||
import Button from './button';
|
||||
import Container from './container';
|
||||
import Img from './img';
|
||||
import IteratorContainer from './iterator-container';
|
||||
import Overlay from './overlay';
|
||||
import Page from './page';
|
||||
import pageFragment from './page-fragment';
|
||||
@ -40,6 +41,7 @@ const ui: Record<string, any> = {
|
||||
overlay: Overlay,
|
||||
'page-fragment': pageFragment,
|
||||
'page-fragment-container': pageFragmentContainer,
|
||||
'iterator-container': IteratorContainer,
|
||||
};
|
||||
|
||||
export default ui;
|
||||
|
24
packages/ui-react/src/iterator-container/index.ts
Normal file
24
packages/ui-react/src/iterator-container/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import IteratorContainer from './src/IteratorContainer';
|
||||
|
||||
export { default as config } from './src/formConfig';
|
||||
export { default as value } from './src/initValue';
|
||||
|
||||
export default IteratorContainer;
|
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { MContainer } from '@tmagic/schema';
|
||||
|
||||
import useApp from '../../useApp';
|
||||
|
||||
interface IteratorContainerProps extends MContainer {
|
||||
config: MContainer & {
|
||||
type: 'iterator-container';
|
||||
iteratorData: any[];
|
||||
dsField: string[];
|
||||
itemConfig: {
|
||||
layout: string;
|
||||
style: Record<string, string | number>;
|
||||
};
|
||||
};
|
||||
className: string;
|
||||
style: Record<string, any>;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const IteratorContainer: React.FC<IteratorContainerProps> = ({ config, id }) => {
|
||||
const { app } = useApp({ config });
|
||||
|
||||
const { iteratorData = [] } = config;
|
||||
|
||||
if (app?.platform === 'editor' && !iteratorData.length) {
|
||||
iteratorData.push({});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const MagicUiComp = app?.resolveComponent('container');
|
||||
return (
|
||||
<div
|
||||
id={`${id || config.id || ''}`}
|
||||
className="magic-ui-iterator-container"
|
||||
style={app?.transformStyle(config.style || {})}
|
||||
>
|
||||
{iteratorData.map((itemData, index) => {
|
||||
const itemConfig = {
|
||||
items: app?.dataSourceManager?.compliedIteratorItems(itemData, config.items, config.dsField) ?? config.items,
|
||||
id: '',
|
||||
style: {
|
||||
...config.itemConfig.style,
|
||||
position: 'relative',
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
};
|
||||
return <MagicUiComp config={itemConfig} key={index}></MagicUiComp>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
IteratorContainer.displayName = 'magic-ui-iterator-container';
|
||||
|
||||
export default IteratorContainer;
|
110
packages/ui-react/src/iterator-container/src/formConfig.ts
Normal file
110
packages/ui-react/src/iterator-container/src/formConfig.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'iteratorData',
|
||||
text: '数据源数据',
|
||||
value: 'value',
|
||||
fieldType: ['array'],
|
||||
checkStrictly: false,
|
||||
type: 'data-source-field-select',
|
||||
onChange: (vm: any, v: string[] = [], { model }: any) => {
|
||||
const [dsId, ...keys] = v;
|
||||
model.dsField = [dsId.replace(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, ''), ...keys];
|
||||
return v;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dsField',
|
||||
type: 'hidden',
|
||||
},
|
||||
{
|
||||
type: 'panel',
|
||||
title: '子项配置',
|
||||
name: 'itemConfig',
|
||||
items: [
|
||||
{
|
||||
name: 'layout',
|
||||
text: '容器布局',
|
||||
type: 'select',
|
||||
defaultValue: 'absolute',
|
||||
options: [
|
||||
{ value: 'absolute', text: '绝对定位' },
|
||||
{ value: 'relative', text: '流式布局', disabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'fieldset',
|
||||
legend: '样式',
|
||||
name: 'style',
|
||||
items: [
|
||||
{
|
||||
name: 'width',
|
||||
text: '宽度',
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
text: '高度',
|
||||
},
|
||||
{
|
||||
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' },
|
||||
],
|
||||
},
|
||||
{
|
||||
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%',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
31
packages/ui-react/src/iterator-container/src/initValue.ts
Normal file
31
packages/ui-react/src/iterator-container/src/initValue.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default {
|
||||
style: {
|
||||
width: '375',
|
||||
height: '100',
|
||||
},
|
||||
itemConfig: {
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
items: [],
|
||||
};
|
@ -19,11 +19,12 @@
|
||||
import Button from './button';
|
||||
import Container from './container';
|
||||
import Img from './img';
|
||||
import IteratorContainer from './iterator-container';
|
||||
import Overlay from './overlay';
|
||||
import Page from './page';
|
||||
import PageFragment from './page-fragment';
|
||||
import PageFragmentContainer from './page-fragment-container';
|
||||
import Qrcode from './qrcode';
|
||||
import QRcode from './qrcode';
|
||||
import Text from './text';
|
||||
|
||||
const ui: Record<string, any> = {
|
||||
@ -32,10 +33,11 @@ const ui: Record<string, any> = {
|
||||
button: Button,
|
||||
text: Text,
|
||||
img: Img,
|
||||
qrcode: Qrcode,
|
||||
qrcode: QRcode,
|
||||
overlay: Overlay,
|
||||
'page-fragment': PageFragment,
|
||||
'page-fragment-container': PageFragmentContainer,
|
||||
'page-fragment': PageFragment,
|
||||
'iterator-container': IteratorContainer,
|
||||
};
|
||||
|
||||
export default ui;
|
||||
|
24
packages/ui-vue2/src/iterator-container/index.ts
Normal file
24
packages/ui-vue2/src/iterator-container/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import IteratorContainer from './src/IteratorContainer.vue';
|
||||
|
||||
export { default as config } from './src/formConfig';
|
||||
export { default as value } from './src/initValue';
|
||||
|
||||
export default IteratorContainer;
|
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="magic-ui-iterator-container" :id="`${config.id || ''}`" :style="style">
|
||||
<Container v-for="(item, index) in configs" :key="index" :config="item"></Container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from 'vue';
|
||||
|
||||
import Core from '@tmagic/core';
|
||||
import type { MContainer } from '@tmagic/schema';
|
||||
|
||||
import Container from '../../container';
|
||||
import useApp from '../../useApp';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config: MContainer & {
|
||||
type: 'iterator-container';
|
||||
iteratorData: any[];
|
||||
dsField: string[];
|
||||
itemConfig: {
|
||||
layout: string;
|
||||
style: Record<string, string | number>;
|
||||
};
|
||||
};
|
||||
model?: any;
|
||||
}>(),
|
||||
{
|
||||
model: () => ({}),
|
||||
},
|
||||
);
|
||||
|
||||
const app: Core | undefined = inject('app');
|
||||
|
||||
const style = computed(() => app?.transformStyle(props.config.style || {}));
|
||||
|
||||
const configs = computed(() => {
|
||||
const { iteratorData = [] } = props.config;
|
||||
|
||||
if (app?.platform === 'editor' && !iteratorData.length) {
|
||||
iteratorData.push({});
|
||||
}
|
||||
|
||||
return iteratorData.map((itemData) => ({
|
||||
items:
|
||||
app?.dataSourceManager?.compliedIteratorItems(itemData, props.config.items, props.config.dsField) ??
|
||||
props.config.items,
|
||||
id: '',
|
||||
style: {
|
||||
...props.config.itemConfig.style,
|
||||
position: 'relative',
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
useApp({
|
||||
config: props.config,
|
||||
methods: {},
|
||||
});
|
||||
</script>
|
110
packages/ui-vue2/src/iterator-container/src/formConfig.ts
Normal file
110
packages/ui-vue2/src/iterator-container/src/formConfig.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'iteratorData',
|
||||
text: '数据源数据',
|
||||
value: 'value',
|
||||
fieldType: ['array'],
|
||||
checkStrictly: false,
|
||||
type: 'data-source-field-select',
|
||||
onChange: (vm: any, v: string[] = [], { model }: any) => {
|
||||
const [dsId, ...keys] = v;
|
||||
model.dsField = [dsId.replace(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, ''), ...keys];
|
||||
return v;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dsField',
|
||||
type: 'hidden',
|
||||
},
|
||||
{
|
||||
type: 'panel',
|
||||
title: '子项配置',
|
||||
name: 'itemConfig',
|
||||
items: [
|
||||
{
|
||||
name: 'layout',
|
||||
text: '容器布局',
|
||||
type: 'select',
|
||||
defaultValue: 'absolute',
|
||||
options: [
|
||||
{ value: 'absolute', text: '绝对定位' },
|
||||
{ value: 'relative', text: '流式布局', disabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'fieldset',
|
||||
legend: '样式',
|
||||
name: 'style',
|
||||
items: [
|
||||
{
|
||||
name: 'width',
|
||||
text: '宽度',
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
text: '高度',
|
||||
},
|
||||
{
|
||||
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' },
|
||||
],
|
||||
},
|
||||
{
|
||||
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%',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
31
packages/ui-vue2/src/iterator-container/src/initValue.ts
Normal file
31
packages/ui-vue2/src/iterator-container/src/initValue.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default {
|
||||
style: {
|
||||
width: '375',
|
||||
height: '100',
|
||||
},
|
||||
itemConfig: {
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
items: [],
|
||||
};
|
@ -19,11 +19,12 @@
|
||||
import Button from './button';
|
||||
import Container from './container';
|
||||
import Img from './img';
|
||||
import IteratorContainer from './iterator-container';
|
||||
import Overlay from './overlay';
|
||||
import Page from './page';
|
||||
import PageFragment from './page-fragment';
|
||||
import PageFragmentContainer from './page-fragment-container';
|
||||
import Qrcode from './qrcode';
|
||||
import QRcode from './qrcode';
|
||||
import Text from './text';
|
||||
|
||||
const ui: Record<string, any> = {
|
||||
@ -32,10 +33,11 @@ const ui: Record<string, any> = {
|
||||
button: Button,
|
||||
text: Text,
|
||||
img: Img,
|
||||
qrcode: Qrcode,
|
||||
qrcode: QRcode,
|
||||
overlay: Overlay,
|
||||
'page-fragment-container': PageFragmentContainer,
|
||||
'page-fragment': PageFragment,
|
||||
'iterator-container': IteratorContainer,
|
||||
};
|
||||
|
||||
export default ui;
|
||||
|
24
packages/ui/src/iterator-container/index.ts
Normal file
24
packages/ui/src/iterator-container/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import IteratorContainer from './src/IteratorContainer.vue';
|
||||
|
||||
export { default as config } from './src/formConfig';
|
||||
export { default as value } from './src/initValue';
|
||||
|
||||
export default IteratorContainer;
|
63
packages/ui/src/iterator-container/src/IteratorContainer.vue
Normal file
63
packages/ui/src/iterator-container/src/IteratorContainer.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="magic-ui-iterator-container" :id="`${config.id || ''}`" :style="style">
|
||||
<Container v-for="(item, index) in configs" :key="index" :config="item"></Container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from 'vue';
|
||||
|
||||
import Core from '@tmagic/core';
|
||||
import type { MContainer } from '@tmagic/schema';
|
||||
|
||||
import Container from '../../container';
|
||||
import useApp from '../../useApp';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config: MContainer & {
|
||||
type: 'iterator-container';
|
||||
iteratorData: any[];
|
||||
dsField: string[];
|
||||
itemConfig: {
|
||||
layout: string;
|
||||
style: Record<string, string | number>;
|
||||
};
|
||||
};
|
||||
model?: any;
|
||||
}>(),
|
||||
{
|
||||
model: () => ({}),
|
||||
},
|
||||
);
|
||||
|
||||
const app: Core | undefined = inject('app');
|
||||
|
||||
const style = computed(() => app?.transformStyle(props.config.style || {}));
|
||||
|
||||
const configs = computed(() => {
|
||||
const { iteratorData = [] } = props.config;
|
||||
|
||||
if (app?.platform === 'editor' && !iteratorData.length) {
|
||||
iteratorData.push({});
|
||||
}
|
||||
|
||||
return iteratorData.map((itemData) => ({
|
||||
items:
|
||||
app?.dataSourceManager?.compliedIteratorItems(itemData, props.config.items, props.config.dsField) ??
|
||||
props.config.items,
|
||||
id: '',
|
||||
style: {
|
||||
...props.config.itemConfig.style,
|
||||
position: 'relative',
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
useApp({
|
||||
config: props.config,
|
||||
methods: {},
|
||||
});
|
||||
</script>
|
110
packages/ui/src/iterator-container/src/formConfig.ts
Normal file
110
packages/ui/src/iterator-container/src/formConfig.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'iteratorData',
|
||||
text: '数据源数据',
|
||||
value: 'value',
|
||||
fieldType: ['array'],
|
||||
checkStrictly: false,
|
||||
type: 'data-source-field-select',
|
||||
onChange: (vm: any, v: string[] = [], { model }: any) => {
|
||||
const [dsId, ...keys] = v;
|
||||
model.dsField = [dsId.replace(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, ''), ...keys];
|
||||
return v;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dsField',
|
||||
type: 'hidden',
|
||||
},
|
||||
{
|
||||
type: 'panel',
|
||||
title: '子项配置',
|
||||
name: 'itemConfig',
|
||||
items: [
|
||||
{
|
||||
name: 'layout',
|
||||
text: '容器布局',
|
||||
type: 'select',
|
||||
defaultValue: 'absolute',
|
||||
options: [
|
||||
{ value: 'absolute', text: '绝对定位' },
|
||||
{ value: 'relative', text: '流式布局', disabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'fieldset',
|
||||
legend: '样式',
|
||||
name: 'style',
|
||||
items: [
|
||||
{
|
||||
name: 'width',
|
||||
text: '宽度',
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
text: '高度',
|
||||
},
|
||||
{
|
||||
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' },
|
||||
],
|
||||
},
|
||||
{
|
||||
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%',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
31
packages/ui/src/iterator-container/src/initValue.ts
Normal file
31
packages/ui/src/iterator-container/src/initValue.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default {
|
||||
style: {
|
||||
width: '375',
|
||||
height: '100',
|
||||
},
|
||||
itemConfig: {
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
items: [],
|
||||
};
|
@ -18,7 +18,7 @@
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { cloneDeep, get as objectGet, set as objectSet } from 'lodash-es';
|
||||
import { cloneDeep, set as objectSet } from 'lodash-es';
|
||||
|
||||
import type { DataSchema, DataSourceDeps, Id, MComponent, MNode } from '@tmagic/schema';
|
||||
import { NodeType } from '@tmagic/schema';
|
||||
@ -165,7 +165,18 @@ export const guid = (digit = 8): string =>
|
||||
return v.toString(16);
|
||||
});
|
||||
|
||||
export const getValueByKeyPath: any = (keys: string, data: Record<string | number, any> = {}) => objectGet(data, keys);
|
||||
export const getValueByKeyPath: any = (keys = '', data: Record<string | number, any> = {}) =>
|
||||
// 将 array[0] 转成 array.0
|
||||
keys
|
||||
.replaceAll(/\[(\d+)\]/g, '.$1')
|
||||
.split('.')
|
||||
.reduce((accumulator, currentValue: any) => {
|
||||
if (isObject(accumulator) || Array.isArray(accumulator)) {
|
||||
return accumulator[currentValue];
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}, data);
|
||||
|
||||
export const setValueByKeyPath: any = (keys: string, value: any, data: Record<string | number, any> = {}) =>
|
||||
objectSet(data, keys, value);
|
||||
@ -238,6 +249,8 @@ export const replaceChildNode = (newNode: MNode, data?: MNode[], parentId?: Id)
|
||||
parent.items.splice(index, 1, newNode);
|
||||
};
|
||||
|
||||
export const DSL_NODE_KEY_COPY_PREFIX = '__magic__';
|
||||
|
||||
export const compiledNode = (
|
||||
compile: (value: any) => any,
|
||||
node: MNode,
|
||||
@ -252,10 +265,8 @@ export const compiledNode = (
|
||||
keys = dep?.[node.id].keys || [];
|
||||
}
|
||||
|
||||
const keyPrefix = '__magic__';
|
||||
|
||||
keys.forEach((key) => {
|
||||
const cacheKey = `${keyPrefix}${key}`;
|
||||
const cacheKey = `${DSL_NODE_KEY_COPY_PREFIX}${key}`;
|
||||
|
||||
const value = getValueByKeyPath(key, node);
|
||||
let templateValue = getValueByKeyPath(cacheKey, node);
|
||||
@ -276,10 +287,6 @@ export const compiledNode = (
|
||||
setValueByKeyPath(key, newValue, node);
|
||||
});
|
||||
|
||||
if (Array.isArray(node.items)) {
|
||||
node.items.forEach((item) => compiledNode(compile, item, dataSourceDeps));
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
|
@ -31,7 +31,7 @@ export default defineComponent({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.m-editor-nav-menu {
|
||||
justify-content: flex-end;
|
||||
height: 35px;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FolderOpened, Grid, PictureFilled, SwitchButton, Ticket, Tickets } from '@element-plus/icons-vue';
|
||||
import { Files, FolderOpened, Grid, PictureFilled, SwitchButton, Ticket, Tickets } from '@element-plus/icons-vue';
|
||||
|
||||
export default [
|
||||
{
|
||||
@ -19,6 +19,11 @@ export default [
|
||||
text: '页面片容器',
|
||||
type: 'page-fragment-container',
|
||||
},
|
||||
{
|
||||
icon: Files,
|
||||
text: '迭代器容器',
|
||||
type: 'iterator-container',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -82,11 +82,6 @@ export default {
|
||||
age: 12, // 参数
|
||||
},
|
||||
},
|
||||
{
|
||||
actionType: 'comp',
|
||||
to: 'overlay_2159', // 联动组件id
|
||||
method: 'openOverlay', // 联动组件方法
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -369,13 +364,67 @@ export default {
|
||||
description: '按钮',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'text',
|
||||
title: '按钮文案',
|
||||
type: 'string',
|
||||
description: '',
|
||||
enable: true,
|
||||
defaultValue: '打开弹窗',
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
title: 'array',
|
||||
type: 'array',
|
||||
description: '',
|
||||
enable: true,
|
||||
defaultValue: [
|
||||
{
|
||||
a: 1,
|
||||
},
|
||||
{
|
||||
a: 2,
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'a',
|
||||
title: 'a',
|
||||
type: 'number',
|
||||
description: '',
|
||||
enable: true,
|
||||
defaultValue: 1,
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'object',
|
||||
title: 'object',
|
||||
type: 'object',
|
||||
description: '',
|
||||
enable: true,
|
||||
defaultValue: {
|
||||
a: 1,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'a',
|
||||
title: 'a',
|
||||
type: 'number',
|
||||
description: '',
|
||||
enable: true,
|
||||
defaultValue: 1,
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
methods: [],
|
||||
events: '',
|
||||
mocks: [],
|
||||
beforeRequest: '',
|
||||
afterResponse: '',
|
||||
},
|
||||
],
|
||||
dataSourceDeps: {
|
||||
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -668,6 +668,9 @@ importers:
|
||||
'@tmagic/schema':
|
||||
specifier: 1.3.16
|
||||
version: link:../schema
|
||||
'@tmagic/utils':
|
||||
specifier: 1.3.16
|
||||
version: link:../utils
|
||||
qrcode:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user