feat/feat-apiselected (#1132)

Reviewed-on: daoyoucloud/tachybase#1132
Co-authored-by: bai.zixv <bai.zixv@foxmail.com>
Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
bai.zixv 2024-06-06 11:48:27 +08:00 committed by sealday
parent 6df452378f
commit 03a1dfdf1e
12 changed files with 61 additions and 47 deletions

View File

@ -32,17 +32,17 @@ export const ActionAreaStub = () => {
export const ActionAreaPlayer = ({ children }) => { export const ActionAreaPlayer = ({ children }) => {
const { visible, setVisible } = useActionContext(); const { visible, setVisible } = useActionContext();
const { prevSetVisible, setPrevSetVisible, ref } = useContext(ActionAreaContext); const { prevSetVisible, setPrevSetVisible, ref = { current: null } } = useContext(ActionAreaContext);
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
setPrevSetVisible(() => { setPrevSetVisible?.(() => {
prevSetVisible?.(false); prevSetVisible?.(false);
return setVisible; return setVisible;
}); });
} }
}, [visible]); }, [visible]);
return visible ? createPortal(children, ref.current) : null; return visible && ref.current ? createPortal(children, ref.current) : null;
}; };
export const ActionArea: ComposedActionDrawer = observer( export const ActionArea: ComposedActionDrawer = observer(

View File

@ -19,9 +19,9 @@ const schema = {
'x-acl-action-props': { 'x-acl-action-props': {
skipScopeCheck: true, skipScopeCheck: true,
}, },
'x-action': 'customize:APIRegular',
'x-toolbar': 'ActionSchemaToolbar', 'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:APIRegular', 'x-settings': 'actionSettings:APIRegular',
'x-action': 'customize:APIRegular',
'x-action-settings': { 'x-action-settings': {
bindWorkflow: false, bindWorkflow: false,
updateMode: 'selected', updateMode: 'selected',

View File

@ -54,6 +54,12 @@ export const usePropsAPIRegular = () => {
updateMode, updateMode,
} = actionSchema?.['x-action-settings'] ?? {}; } = actionSchema?.['x-action-settings'] ?? {};
const isUpdateSelected = updateMode === 'selected';
const updateData = {
primaryKey: rowKey || 'id',
targetKeys: selectedRecordKeys,
};
actionField.data = field.data || {}; actionField.data = field.data || {};
actionField.data.loading = true; actionField.data.loading = true;
@ -86,15 +92,14 @@ export const usePropsAPIRegular = () => {
await Promise.all(waitList); await Promise.all(waitList);
modal.confirm({ modal.confirm({
title: lang('Bulk update', { ns: 'client' }), title: lang('Confirm', { ns: 'client' }),
content: content: lang('Trigger workflow?', { ns: 'client' }),
updateMode === 'selected'
? lang('Update selected data?', { ns: 'client' })
: lang('Update all data?', { ns: 'client' }),
async onOk() { async onOk() {
run({ const params = {
filterByTk: bindWorkflow, filterByTk: bindWorkflow,
}); updateData: encodeURIComponent(JSON.stringify(updateData)),
};
run(params);
actionField.data.loading = false; actionField.data.loading = false;
}, },
async onCancel() { async onCancel() {
@ -104,21 +109,3 @@ export const usePropsAPIRegular = () => {
}, },
}; };
}; };
function updateData() {
// const updateData: { filter?: any; values: any; forceUpdate: boolean } = {
// values,
// filter,
// forceUpdate: false,
// };
// if (updateMode === 'selected') {
// if (!selectedRecordKeys?.length) {
// message.error(t('Please select the records to be updated'));
// return;
// }
// updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRecordKeys } }] };
// }
// if (!updateData.filter) {
// updateData.forceUpdate = true;
// }
}

View File

@ -2,8 +2,8 @@ import { Plugin, useCollection } from '@tachybase/client';
import { tval } from '../../../locale'; import { tval } from '../../../locale';
import { APIRegularInitializer } from './APIRegular.schema'; import { APIRegularInitializer } from './APIRegular.schema';
import { APIRegularActionSettings } from './BulkUpdateAction.Settings'; import { APIRegularActionSettings } from './APIRegular.setting';
import { usePropsAPIRegular } from './utils'; import { usePropsAPIRegular } from './hooks';
export class KitAPIRegularUsage extends Plugin { export class KitAPIRegularUsage extends Plugin {
async load() { async load() {

View File

@ -77,7 +77,8 @@ function useUpdateAction() {
}, },
}); });
ctx.setFormValueChanged(false); ctx.setFormValueChanged(false);
ctx.setVisible(false); // ctx.setVisible(false);
message.success('success');
refresh(); refresh();
}, },
}; };
@ -410,7 +411,7 @@ export function NodeDefaultView(props) {
? null ? null
: { : {
type: 'void', type: 'void',
'x-component': 'Action.Drawer.Footer', 'x-component': 'ActionArea.Footer',
properties: { properties: {
cancel: { cancel: {
title: '{{t("Cancel")}}', title: '{{t("Cancel")}}',

View File

@ -42,6 +42,7 @@ const useStyles = createStyles(({ css, token }) => {
} }
.workflow-operator-area { .workflow-operator-area {
overflow-y: scroll;
padding: 8px; padding: 8px;
background-color: white; background-color: white;
width: 800px; width: 800px;

View File

@ -46,7 +46,8 @@ function useUpdateConfigAction() {
}, },
}); });
ctx.setFormValueChanged(false); ctx.setFormValueChanged(false);
ctx.setVisible(false); // ctx.setVisible(false);
message.success('success');
refresh(); refresh();
}, },
}; };
@ -298,7 +299,7 @@ export const TriggerConfig = () => {
? {} ? {}
: { : {
type: 'void', type: 'void',
'x-component': 'Action.Drawer.Footer', 'x-component': 'ActionArea.Footer',
properties: { properties: {
cancel: { cancel: {
title: '{{t("Cancel")}}', title: '{{t("Cancel")}}',

View File

@ -164,10 +164,17 @@ export async function sync(context: Context, next) {
export async function trigger(ctx: Context) { export async function trigger(ctx: Context) {
const plugin = ctx.app.getPlugin(Plugin) as Plugin; const plugin = ctx.app.getPlugin(Plugin) as Plugin;
const workflow = (await ctx.db.getRepository('workflows').findById(ctx.action.params.filterByTk)) as WorkflowModel; const workflow = (await ctx.db.getRepository('workflows').findById(ctx.action.params.filterByTk)) as WorkflowModel;
// NOTE: 这里的updateData是通过前端传过来的需要 decodeURIComponent,
// updateData 的约定结构是形如: updateData: { primaryKey: "id", targetKeys: []}
const updateData = JSON.parse(decodeURIComponent(ctx.action.params?.updateData || ''));
plugin.trigger( plugin.trigger(
workflow, workflow,
{ {
data: {}, data: {
updateData,
httpContext: ctx,
user: ctx?.auth?.user,
},
}, },
{ httpContext: ctx }, { httpContext: ctx },
); );

View File

@ -1,11 +1,12 @@
import { get } from 'lodash'; import { Context as ActionContext, Next } from '@tachybase/actions';
import { BelongsTo, HasOne } from 'sequelize'; import { parseCollectionName } from '@tachybase/data-source-manager';
import { Model, modelAssociationByKey } from '@tachybase/database'; import { Model, modelAssociationByKey } from '@tachybase/database';
import Application, { DefaultContext } from '@tachybase/server'; import Application, { DefaultContext } from '@tachybase/server';
import { Context as ActionContext, Next } from '@tachybase/actions';
import WorkflowPlugin, { Trigger, WorkflowModel, toJSON } from '../..'; import { get } from 'lodash';
import { parseCollectionName } from '@tachybase/data-source-manager'; import { BelongsTo, HasOne } from 'sequelize';
import WorkflowPlugin, { toJSON, Trigger, WorkflowModel } from '../..';
interface Context extends ActionContext, DefaultContext {} interface Context extends ActionContext, DefaultContext {}

View File

@ -15,7 +15,7 @@ interface Context extends ActionContext, DefaultContext {}
export class APIRegularTrigger extends Trigger { export class APIRegularTrigger extends Trigger {
constructor(workflow: WorkflowPlugin) { constructor(workflow: WorkflowPlugin) {
super(workflow); super(workflow);
workflow.app.use(this.middleware, { after: 'dataSource' }); // workflow.app.use(this.middleware, { after: 'dataSource' });
} }
async middleware(context: Context, next: Next) { async middleware(context: Context, next: Next) {

View File

@ -1,4 +1,5 @@
import axios, { AxiosRequestConfig } from 'axios'; import axios, { AxiosRequestConfig } from 'axios';
import _ from 'lodash';
import { FlowNodeModel, Instruction, JOB_STATUS, Processor } from '../..'; import { FlowNodeModel, Instruction, JOB_STATUS, Processor } from '../..';
@ -12,10 +13,14 @@ export type RequestConfig = Pick<AxiosRequestConfig, 'url' | 'method' | 'params'
ignoreFail: boolean; ignoreFail: boolean;
}; };
async function request(config) { async function request(config, context) {
// default headers // default headers
const { url, method = 'POST', data, timeout = 5000 } = config; const { token, origin } = context;
const headers = (config.headers ?? []).reduce((result, header) => { const { url: originUrl, method = 'POST', data, timeout = 5000 } = config;
const url = originUrl.startsWith('http') ? originUrl : `${origin}${originUrl}`;
let headers = (config.headers ?? []).reduce((result, header) => {
if (header.name.toLowerCase() === 'content-type') { if (header.name.toLowerCase() === 'content-type') {
return result; return result;
} }
@ -29,6 +34,11 @@ async function request(config) {
// TODO(feat): only support JSON type for now, should support others in future // TODO(feat): only support JSON type for now, should support others in future
headers['Content-Type'] = 'application/json'; headers['Content-Type'] = 'application/json';
// let temp = context.get('headers');
headers = {
Authorization: 'Bearer ' + token,
...headers,
};
return axios.request({ return axios.request({
url, url,
method, method,
@ -41,13 +51,19 @@ async function request(config) {
export default class extends Instruction { export default class extends Instruction {
async run(node: FlowNodeModel, prevJob, processor: Processor) { async run(node: FlowNodeModel, prevJob, processor: Processor) {
const httpContext = _.get(processor, 'execution.dataValues.context.data.httpContext', {});
const userId = _.get(processor, 'execution.dataValues.context.data.user.id', 1);
const origin = _.get(httpContext, 'request.header.origin', '');
const token = this.workflow.app.authManager.jwt.sign({ userId });
const context = { token, origin };
const config = processor.getParsedValue(node.config, node.id) as RequestConfig; const config = processor.getParsedValue(node.config, node.id) as RequestConfig;
const { workflow } = processor.execution; const { workflow } = processor.execution;
const sync = this.workflow.isWorkflowSync(workflow); const sync = this.workflow.isWorkflowSync(workflow);
if (sync) { if (sync) {
try { try {
const response = await request(config); const response = await request(config, context);
return { return {
status: JOB_STATUS.RESOLVED, status: JOB_STATUS.RESOLVED,
@ -69,7 +85,7 @@ export default class extends Instruction {
}); });
// eslint-disable-next-line promise/catch-or-return // eslint-disable-next-line promise/catch-or-return
request(config) request(config, context)
.then((response) => { .then((response) => {
job.set({ job.set({
status: JOB_STATUS.RESOLVED, status: JOB_STATUS.RESOLVED,