feat(core,data-source,ui,ui-react,ui-vue2,utils,runtime): 解析显示条件配置

This commit is contained in:
roymondchen 2023-08-04 19:50:44 +08:00
parent c389c614d7
commit 92df80e711
16 changed files with 156 additions and 54 deletions

View File

@ -18,14 +18,9 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { cloneDeep, has, isEmpty, template } from 'lodash-es'; import { has, isEmpty } from 'lodash-es';
import { import { createDataSourceManager, DataSourceManager, RequestFunction } from '@tmagic/data-source';
createDataSourceManager,
DataSourceManager,
DataSourceManagerData,
RequestFunction,
} from '@tmagic/data-source';
import { import {
ActionType, ActionType,
CodeBlockDSL, CodeBlockDSL,
@ -36,9 +31,7 @@ import {
EventConfig, EventConfig,
Id, Id,
MApp, MApp,
MNode,
} from '@tmagic/schema'; } from '@tmagic/schema';
import { compiledNode } from '@tmagic/utils';
import Env from './Env'; import Env from './Env';
import { bindCommonEventListener, isCommonMethod, triggerCommonMethod } from './events'; import { bindCommonEventListener, isCommonMethod, triggerCommonMethod } from './events';
@ -174,13 +167,9 @@ class App extends EventEmitter {
this.dataSourceManager.destroy(); this.dataSourceManager.destroy();
} }
this.dataSourceManager = createDataSourceManager( this.dataSourceManager = createDataSourceManager(config, this.platform, {
config, request,
(node: MNode, content: DataSourceManagerData) => this.compiledNode(node, content), });
{
request,
},
);
this.codeDsl = config.codeBlocks; this.codeDsl = config.codeBlocks;
this.setPage(curPage || this.page?.data?.id); this.setPage(curPage || this.page?.data?.id);
@ -344,23 +333,6 @@ class App extends EventEmitter {
} }
} }
public compiledNode(node: MNode, content: DataSourceManagerData, sourceId?: Id) {
return compiledNode(
(value: any) => {
if (typeof value === 'string') {
return template(value)(content);
}
if (value?.isBindDataSource && value.dataSourceId) {
return content[value.dataSourceId];
}
return value;
},
cloneDeep(node),
this.dsl?.dataSourceDeps,
sourceId,
);
}
public destroy() { public destroy() {
this.removeAllListeners(); this.removeAllListeners();
this.page = undefined; this.page = undefined;

View File

@ -18,7 +18,10 @@
import EventEmitter from 'events'; import EventEmitter from 'events';
import { DataSourceSchema } from '@tmagic/schema'; import { cloneDeep, template } from 'lodash-es';
import { DataSourceDeps, DataSourceSchema, Id, MNode } from '@tmagic/schema';
import { compiledCond, compiledNode } from '@tmagic/utils';
import { DataSource, HttpDataSource } from './data-sources'; import { DataSource, HttpDataSource } from './data-sources';
import type { DataSourceManagerData, DataSourceManagerOptions, HttpDataSourceSchema, RequestFunction } from './types'; import type { DataSourceManagerData, DataSourceManagerOptions, HttpDataSourceSchema, RequestFunction } from './types';
@ -32,12 +35,17 @@ class DataSourceManager extends EventEmitter {
public dataSourceMap = new Map<string, DataSource>(); public dataSourceMap = new Map<string, DataSource>();
public data: DataSourceManagerData = {}; public data: DataSourceManagerData = {};
public dataSourceDeps: DataSourceDeps = {};
public dataSourceCondDeps: DataSourceDeps = {};
private request?: RequestFunction; private request?: RequestFunction;
constructor(options: DataSourceManagerOptions) { constructor(options: DataSourceManagerOptions) {
super(); super();
this.dataSourceDeps = options.dataSourceDeps || {};
this.dataSourceCondDeps = options.dataSourceCondDeps || {};
if (options.httpDataSourceOptions?.request) { if (options.httpDataSourceOptions?.request) {
this.request = options.httpDataSourceOptions.request; this.request = options.httpDataSourceOptions.request;
} }
@ -102,6 +110,50 @@ class DataSourceManager extends EventEmitter {
}); });
} }
public compiledNode(node: MNode, sourceId?: Id) {
if (node.condResult === false) return node;
if (node.visible === false) return node;
return compiledNode(
(value: any) => {
if (typeof value === 'string') {
return template(value)(this.data);
}
if (value?.isBindDataSource && value.dataSourceId) {
return this.data[value.dataSourceId];
}
return value;
},
cloneDeep(node),
this.dataSourceDeps,
sourceId,
);
}
public compliedConds(node: MNode) {
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, fieldKey] = field;
const fieldValue = this.data[sourceId][fieldKey];
if (!compiledCond(op, fieldValue, value, range)) {
result = false;
break;
}
}
if (result) {
return true;
}
}
return false;
}
public destroy() { public destroy() {
this.removeAllListeners(); this.removeAllListeners();
this.data = {}; this.data = {};

View File

@ -15,11 +15,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import type { MApp, MNode } from '@tmagic/schema'; import { cloneDeep } from 'lodash-es';
import type { MApp } from '@tmagic/schema';
import { getDepNodeIds, getNodes, replaceChildNode } from '@tmagic/utils'; import { getDepNodeIds, getNodes, replaceChildNode } from '@tmagic/utils';
import DataSourceManager from './DataSourceManager'; import DataSourceManager from './DataSourceManager';
import type { DataSourceManagerData, HttpDataSourceOptions } from './types'; import type { HttpDataSourceOptions } from './types';
/** /**
* *
@ -29,26 +31,54 @@ import type { DataSourceManagerData, HttpDataSourceOptions } from './types';
*/ */
export const createDataSourceManager = ( export const createDataSourceManager = (
dsl: MApp, dsl: MApp,
compiledNode = (node: MNode, _content: DataSourceManagerData) => node, platform: string,
httpDataSourceOptions?: Partial<HttpDataSourceOptions>, httpDataSourceOptions?: Partial<HttpDataSourceOptions>,
) => { ) => {
if (!dsl?.dataSources) return; if (!dsl?.dataSources) return;
const dataSourceManager = new DataSourceManager({ const dataSourceManager = new DataSourceManager({
dataSourceConfigs: dsl.dataSources, dataSourceConfigs: dsl.dataSources,
dataSourceDeps: dsl.dataSourceDeps,
dataSourceCondDeps: dsl.dataSourceCondDeps,
httpDataSourceOptions, httpDataSourceOptions,
}); });
if (dsl.dataSources && dsl.dataSourceCondDeps && platform !== 'editor') {
getNodes(getDepNodeIds(dsl.dataSourceCondDeps), dsl.items).forEach((node) => {
node.condResult = dataSourceManager.compliedConds(node);
replaceChildNode(node, dsl!.items);
});
}
if (dsl.dataSources && dsl.dataSourceDeps) { if (dsl.dataSources && dsl.dataSourceDeps) {
getNodes(getDepNodeIds(dsl.dataSourceDeps), dsl.items).forEach((node) => { getNodes(getDepNodeIds(dsl.dataSourceDeps), dsl.items).forEach((node) => {
replaceChildNode(compiledNode(node, dataSourceManager.data), dsl!.items); replaceChildNode(dataSourceManager.compiledNode(node), dsl!.items);
}); });
} }
dataSourceManager.on('change', (sourceId: string) => { dataSourceManager.on('change', (sourceId: string) => {
const dep = dsl.dataSourceDeps?.[sourceId]; const dep = dsl.dataSourceDeps?.[sourceId];
if (!dep) return; const condDep = dsl.dataSourceCondDeps?.[sourceId];
dataSourceManager.emit('update-data', getNodes(Object.keys(dep), dsl.items), sourceId);
if (condDep) {
dataSourceManager.emit(
'update-data',
getNodes(Object.keys(condDep), dsl.items).map((node) => {
const newNode = cloneDeep(node);
newNode.condResult = dataSourceManager.compliedConds(node);
return newNode;
}),
sourceId,
);
}
if (dep) {
dataSourceManager.emit(
'update-data',
getNodes(Object.keys(dep), dsl.items).map((node) => dataSourceManager.compiledNode(node)),
sourceId,
);
}
}); });
return dataSourceManager; return dataSourceManager;

View File

@ -1,4 +1,4 @@
import { DataSourceSchema } from '@tmagic/schema'; import { DataSourceDeps, DataSourceSchema } from '@tmagic/schema';
export interface DataSourceOptions { export interface DataSourceOptions {
schema: DataSourceSchema; schema: DataSourceSchema;
@ -32,6 +32,8 @@ export interface HttpDataSourceOptions {
export interface DataSourceManagerOptions { export interface DataSourceManagerOptions {
dataSourceConfigs: DataSourceSchema[]; dataSourceConfigs: DataSourceSchema[];
dataSourceDeps?: DataSourceDeps;
dataSourceCondDeps?: DataSourceDeps;
httpDataSourceOptions?: Partial<HttpDataSourceOptions>; httpDataSourceOptions?: Partial<HttpDataSourceOptions>;
} }

View File

@ -235,7 +235,7 @@ export const displayTabConfig: TabPaneConfig = {
items: [ items: [
{ {
type: 'groupList', type: 'groupList',
name: 'showCond', name: 'displayConds',
items: [ items: [
{ {
type: 'table', type: 'table',

View File

@ -46,6 +46,7 @@ const Container: React.FC<ContainerProps> = ({ config, id }) => {
if (!MagicUiComp) return null; if (!MagicUiComp) return null;
if (item.visible === false) return null; if (item.visible === false) return null;
if (item.condResult === false) return null;
return ( return (
<MagicUiComp <MagicUiComp

View File

@ -50,6 +50,7 @@ const Page: React.FC<PageProps> = ({ config }) => {
if (!MagicUiComp) return null; if (!MagicUiComp) return null;
if (item.visible === false) return null; if (item.visible === false) return null;
if (item.condResult === false) return null;
return ( return (
<MagicUiComp <MagicUiComp

View File

@ -36,6 +36,7 @@ export default defineComponent({
display: () => { display: () => {
if (props.config.visible === false) return false; if (props.config.visible === false) return false;
if (props.config.condResult === false) return false;
const displayCfg = props.config?.display; const displayCfg = props.config?.display;

View File

@ -34,6 +34,7 @@ const style = computed(() => app?.transformStyle(props.config.style));
const display = () => { const display = () => {
if (props.config.visible === false) return false; if (props.config.visible === false) return false;
if (props.config.condResult === false) return false;
const displayCfg = props.config?.display; const displayCfg = props.config?.display;

View File

@ -295,3 +295,48 @@ export const compiledNode = (
return node; return node;
}; };
export const compiledCond = (op: string, fieldValue: any, value: any, range: [number, number]): boolean => {
switch (op) {
case 'is':
if (!fieldValue) return false;
break;
case 'not':
if (fieldValue) return false;
break;
case '=':
if (fieldValue !== value) return false;
break;
case '!=':
if (fieldValue === value) return false;
break;
case '>':
if (fieldValue <= value) return false;
break;
case '>=':
if (fieldValue < value) return false;
break;
case '<':
if (fieldValue >= value) return false;
break;
case '<=':
if (fieldValue > value) return false;
break;
case 'between':
if (fieldValue < range[0] || fieldValue > range[1]) return false;
break;
case 'not_between':
if (fieldValue >= range[0] && fieldValue <= range[1]) return false;
break;
case 'include':
if (!fieldValue.includes(value)) return false;
break;
case 'not_include':
if (fieldValue.includes(value)) return false;
break;
default:
break;
}
return true;
};

View File

@ -31,10 +31,9 @@ function App() {
const [config, setConfig] = useState(app.page.data); const [config, setConfig] = useState(app.page.data);
app.dataSourceManager?.on('update-data', (nodes: MNode[], sourceId: string) => { app.dataSourceManager?.on('update-data', (nodes: MNode[]) => {
nodes.forEach((node) => { nodes.forEach((node) => {
const newNode = app.compiledNode(node, app.dataSourceManager?.data || {}, sourceId); replaceChildNode(node, [config]);
replaceChildNode(newNode, [config]);
setConfig(cloneDeep(config)); setConfig(cloneDeep(config));
}); });
}); });

View File

@ -108,7 +108,7 @@ const operations = {
}, },
update({ config, root }: UpdateData) { update({ config, root }: UpdateData) {
replaceChildNode(app.compiledNode(config, app.dataSourceManager?.data || {}), root.items); replaceChildNode(app.dataSourceManager?.compiledNode(config) || config, root.items);
updateConfig(cloneDeep(root)); updateConfig(cloneDeep(root));
}, },

View File

@ -16,10 +16,9 @@ export default defineComponent({
const app = inject<Core | undefined>('app'); const app = inject<Core | undefined>('app');
const pageConfig = reactive(app?.page?.data || {}); const pageConfig = reactive(app?.page?.data || {});
app?.dataSourceManager?.on('update-data', (nodes: MNode[], sourceId: string) => { app?.dataSourceManager?.on('update-data', (nodes: MNode[]) => {
nodes.forEach((node) => { nodes.forEach((node) => {
const newNode = app.compiledNode(node, app.dataSourceManager?.data || {}, sourceId); replaceChildNode(reactive(node), [pageConfig as MNode]);
replaceChildNode(reactive(newNode), [pageConfig as MNode]);
}); });
}); });

View File

@ -88,7 +88,7 @@ export default defineComponent({
update({ config, parentId }: UpdateData) { update({ config, parentId }: UpdateData) {
if (!root.value || !app) throw new Error('error'); if (!root.value || !app) throw new Error('error');
const newNode = app.compiledNode(config, app.dataSourceManager?.data || {}); const newNode = app.dataSourceManager?.compiledNode(config) || config;
replaceChildNode(reactive(newNode), [root.value], parentId); replaceChildNode(reactive(newNode), [root.value], parentId);
const nodeInstance = app.page?.getNode(config.id); const nodeInstance = app.page?.getNode(config.id);

View File

@ -16,10 +16,9 @@ export default defineComponent({
const app = inject<Core | undefined>('app'); const app = inject<Core | undefined>('app');
const pageConfig = reactive(app?.page?.data || {}); const pageConfig = reactive(app?.page?.data || {});
app?.dataSourceManager?.on('update-data', (nodes: MNode[], sourceId: string) => { app?.dataSourceManager?.on('update-data', (nodes: MNode[]) => {
nodes.forEach((node) => { nodes.forEach((node) => {
const newNode = app.compiledNode(node, app.dataSourceManager?.data || {}, sourceId); replaceChildNode(reactive(node), [pageConfig as MNode]);
replaceChildNode(reactive(newNode), [pageConfig as MNode]);
}); });
}); });

View File

@ -85,7 +85,7 @@ window.magic?.onRuntimeReady({
update({ config, parentId }: UpdateData) { update({ config, parentId }: UpdateData) {
if (!root.value || !app) throw new Error('error'); if (!root.value || !app) throw new Error('error');
const newNode = app.compiledNode(config, app.dataSourceManager?.data || {}); const newNode = app.dataSourceManager?.compiledNode(config) || config;
replaceChildNode(reactive(newNode), [root.value], parentId); replaceChildNode(reactive(newNode), [root.value], parentId);
const nodeInstance = app.page?.getNode(config.id); const nodeInstance = app.page?.getNode(config.id);