fix: demo

This commit is contained in:
gchust 2025-04-02 10:14:31 +08:00
parent f1305b7592
commit e0ac5ec62d
5 changed files with 184 additions and 32 deletions

View File

@ -1,22 +1,51 @@
import { useFieldSchema } from '@formily/react';
import { useBlockResource } from '@nocobase/client';
import { useApplyFilter } from '@nocobase/plugin-event-filter-system/client';
import { Spin, Table } from 'antd';
import { EventContext, useAddEventListener, useApplyFilter } from '@nocobase/plugin-event-filter-system/client';
import { Spin, Table, Button, Space, Row, Flex } from 'antd';
import React, { useMemo } from 'react';
import { useApp } from '@nocobase/client';
const DemoTable = (props) => {
const { done, result } = useApplyFilter('core:block:table', {
props: props,
const { done, result } = useApplyFilter('core:block:table', { props });
const fieldSchema = useFieldSchema();
const resource = useBlockResource();
const app = useApp();
useAddEventListener('core:block:table:refresh', (ctx: EventContext) => {
console.log('core:block:table:refresh', ctx);
resource.list();
});
const actionEventContext: EventContext = useMemo(() => {
return {
source: {
uischema: fieldSchema.toJSON(),
},
};
}, [fieldSchema]);
if (!done) {
return <Spin />;
}
const columns = result?.columns || [];
const data = result?.data?.data || [];
const actions = result?.actions || [];
return <Table style={{ height: result?.height, width: result?.width }} columns={columns} dataSource={data} />;
return (
<Flex vertical style={{ height: result?.height, width: result?.width }}>
<Row justify="end" style={{ marginBottom: '16px' }}>
<Space>
{actions.map((action) => (
<Button key={action.label} onClick={async () => await action.handle(actionEventContext)}>
{action.label}
</Button>
))}
</Space>
</Row>
<Table style={{ width: '100%' }} columns={columns} dataSource={data} />
</Flex>
);
};
export default DemoTable;

View File

@ -96,8 +96,22 @@ const TableDataBlockInitializer = () => {
},
'x-component-settings': {
height: '1000px',
width: '50%',
width: '100%',
linkageRules: {},
actions: [
{
label: '添加',
type: 'addNew',
},
{
label: '删除',
type: 'delete',
},
{
label: '刷新',
type: 'refresh',
},
],
},
},
},

View File

@ -16,6 +16,7 @@ import {
EventListenerOptions,
ApplyFilterOptions,
FilterContext,
EventContext,
} from './types';
import { useFieldSchema } from '@formily/react';
@ -46,6 +47,7 @@ export function useAddFilter(name: string, filter: FilterFunction, options: Filt
* Hook that returns a function that will apply filters to a given value
*
* @param name - Filter name to apply
* @param options - Apply filter options
* @returns A function that applies the filter and returns a Promise
*/
@ -91,31 +93,43 @@ export const useApplyFilter = (name: string, options: ApplyFilterOptions) => {
};
/**
* Hook for registering an event listener that will be automatically unregistered on component unmount
*
* (ctx) (options)
*
* @param eventName - Event name(s) to listen for
* @param listener - Event listener function
* @param options - Event listener options
* @param deps - Dependency array for memoizing the listener function
* :
* 1. (ctx.target )
* 2. (ctx.target ):
* a. ( options.uischema)
*
*/
export function useEventListener(
eventName: string | string[],
listener: EventListener,
options: EventListenerOptions = {},
deps: any[] = [],
) {
function defaultListenerFilter(ctx: EventContext, options: EventListenerOptions): boolean {
// 1. 多播: 分发时未指定目标, 监听器运行。
if (!ctx.target) {
return true;
}
// 2. 单播: 分发时指定了目标。检查监听器选项。
if (options?.uischema) {
const targetSchema = ctx.target.uischema;
// 基本检查:两个 schema 都存在且具有匹配的 'x-uid'
return !!(targetSchema && options.uischema['x-uid'] && options.uischema['x-uid'] === targetSchema['x-uid']);
} else {
return false;
}
}
export function useAddEventListener(event: string | string[], handler: EventListener, options?: EventListenerOptions) {
const fieldSchema = useFieldSchema();
const app = useApp();
// Memoize the listener function based on deps
const memoizedListener = useCallback(listener, [listener, ...deps]);
useEffect(() => {
// Register the listener on mount
const unsubscribe = app.eventManager.on(eventName, memoizedListener, options);
// Unregister on unmount
const unsubscribe = app.eventManager.on(event, handler, {
filter: defaultListenerFilter,
uischema: fieldSchema.toJSON(),
...options,
});
return unsubscribe;
}, [app, eventName, memoizedListener, options]);
}, [handler, app, event, fieldSchema]);
}
/**

View File

@ -12,8 +12,9 @@ import { EventManager } from './event-manager';
import { FilterManager } from './filter-manager';
import { Resource } from '@nocobase/resourcer';
import { ComponentProps } from 'react';
import { LinkageRuleSettings, LinkageRuleItem } from './types';
import { LinkageRuleSettings, LinkageRuleItem, EventContext } from './types';
import React from 'react';
import { Modal, Form, Input } from 'antd';
export * from './event-manager';
export * from './filter-manager';
export * from './hooks';
@ -106,6 +107,7 @@ export class PluginEventFilterSystemClient extends Plugin {
this.app.eventManager = this.eventManager;
this.app.filterManager = this.filterManager;
this.loadDefaultFilters();
this.loadDefaultEventListeners();
}
loadDefaultFilters() {
@ -146,18 +148,30 @@ export class PluginEventFilterSystemClient extends Plugin {
};
});
this.filterManager.addFilter('core:block:table', async function actionsFilter(input, ctx) {
this.filterManager.addFilter('core:block:table', async (input, ctx) => {
// get actions from settings and input
const { settings } = ctx;
const linkageRules = settings?.linkageRules;
const actionSettings = settings?.actions;
const actions = [];
const handles = {
addNew: (ctx: EventContext) => {
this.eventManager.dispatchEvent('core:block:record:addNew', ctx);
},
delete: (ctx: EventContext) => {
this.eventManager.dispatchEvent('core:block:table:record:delete', ctx);
},
refresh: (ctx: EventContext) => {
this.eventManager.dispatchEvent('core:block:table:refresh', ctx);
},
};
Object.keys(actionSettings || {}).forEach((key) => {
const action = actionSettings[key];
actions.push({
...action,
const action = {
...actionSettings[key],
...linkageRules?.['actions'],
});
};
action['handle'] = handles[action.type];
actions.push(action);
});
return {
@ -204,6 +218,87 @@ export class PluginEventFilterSystemClient extends Plugin {
};
});
}
loadDefaultEventListeners() {
this.eventManager.on('core:block:record:addNew', (ctx: EventContext) => {
console.log('core:block:record:addNew', ctx);
// Simple approach using Modal.open with form elements without Form component
const modal = Modal.info({
title: '新建记录',
icon: null,
content: (
<div style={{ marginTop: 16 }}>
<div style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 8 }}>
<label></label>
</div>
<Input placeholder="请输入名称" id="record-name-input" />
</div>
<div>
<div style={{ marginBottom: 8 }}>
<label></label>
</div>
<Input.TextArea placeholder="请输入描述" rows={4} id="record-description-input" />
</div>
</div>
),
okText: '确定',
cancelText: '取消',
// Use footer to replace the default buttons
footer: (
<div style={{ textAlign: 'right', marginTop: 24 }}>
<button
onClick={() => {
modal.destroy();
}}
style={{
marginRight: 8,
padding: '4px 15px',
backgroundColor: 'white',
border: '1px solid #d9d9d9',
borderRadius: 2,
cursor: 'pointer',
}}
>
</button>
<button
onClick={() => {
const nameInput = document.getElementById('record-name-input') as HTMLInputElement;
const descInput = document.getElementById('record-description-input') as HTMLTextAreaElement;
if (nameInput && nameInput.value) {
const values = {
name: nameInput.value,
description: descInput ? descInput.value : '',
};
console.log('Form values:', values);
modal.destroy();
} else {
// Highlight the name input if empty
if (nameInput) {
nameInput.style.borderColor = 'red';
nameInput.focus();
}
}
}}
style={{
padding: '4px 15px',
backgroundColor: '#1677ff',
border: 'none',
borderRadius: 2,
color: 'white',
cursor: 'pointer',
}}
>
</button>
</div>
),
});
});
}
}
export default PluginEventFilterSystemClient;

View File

@ -47,7 +47,7 @@ export interface EventContext<T = any> {
};
// 用于收集事件监听器的输出结果
results: Record<string, any>;
results?: Record<string, any>;
}
export interface EventListenerOptions {