diff --git a/client/packages/lowcoder-design/src/components/keyValueList.tsx b/client/packages/lowcoder-design/src/components/keyValueList.tsx
index 7bd9f317a..6cedce3c4 100644
--- a/client/packages/lowcoder-design/src/components/keyValueList.tsx
+++ b/client/packages/lowcoder-design/src/components/keyValueList.tsx
@@ -76,20 +76,25 @@ export const KeyValueList = (props: {
list: ReactNode[];
onAdd: () => void;
onDelete: (item: ReactNode, index: number) => void;
+ isStatic?: boolean;
}) => (
<>
{props.list.map((item, index) => (
{item}
- props.list.length > 1 && props.onDelete(item, index)}
- $forbidden={props.list.length === 1}
- />
+ {!props.isStatic &&
+ props.list.length > 1 && props.onDelete(item, index)}
+ $forbidden={props.list.length === 1}
+ />
+ }
))}
+ {!props.isStatic &&
{trans("addItem")}
+ }
>
);
diff --git a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx
index 720fc93fe..cc4018bd5 100644
--- a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx
+++ b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx
@@ -7,10 +7,100 @@ import { BranchDiv, Dropdown } from "lowcoder-design";
import { BottomResTypeEnum } from "types/bottomRes";
import { getPromiseAfterDispatch } from "util/promiseUtils";
import { trans } from "i18n";
+import {keyValueListControl, keyValueListToSearchStr, withDefault} from "lowcoder-sdk";
+import {KeyValue} from "@lowcoder-ee/types/common";
+import { useCallback, useContext, useEffect, useMemo } from "react";
+const ExecuteQueryPropertyView = ({
+ comp,
+ placement,
+}: {
+ comp: any,
+ placement?: "query" | "table"
+}) => {
+ const getQueryOptions = useCallback((editorState?: EditorState) => {
+ const options: { label: string; value: string; variable?: Record }[] =
+ editorState
+ ?.queryCompInfoList()
+ .map((info) => {
+ return {
+ label: info.name,
+ value: info.name,
+ variable: info.data.variable,
+ }
+ })
+ .filter(
+ // Filter out the current query under query
+ (option) => {
+ if (
+ placement === "query" &&
+ editorState.selectedBottomResType === BottomResTypeEnum.Query
+ ) {
+ return option.value !== editorState.selectedBottomResName;
+ }
+ return true;
+ }
+ ) || [];
+
+ // input queries
+ editorState
+ ?.getModuleLayoutComp()
+ ?.getInputs()
+ .forEach((i) => {
+ const { name, type } = i.getView();
+ if (type === InputTypeEnum.Query) {
+ options.push({ label: name, value: name });
+ }
+ });
+ return options;
+ }, [placement]);
+
+ const getVariableOptions = useCallback((editorState?: EditorState) => {
+ return comp.children.queryVariables.propertyView({
+ label: trans("eventHandler.queryVariables"),
+ layout: "vertical",
+ isStatic: true,
+ keyFixed: true,
+ });
+ }, [comp.children.queryVariables.getView()])
+
+ return (
+ <>
+
+
+ {(editorState) => (
+ <>
+ {
+ const options = getQueryOptions(editorState);
+ const selectedQuery = options.find(option => option.value === value);
+ const variables = selectedQuery ? Object.keys(selectedQuery.variable || {}) : [];
+ comp.dispatchChangeValueAction({
+ queryName: value,
+ queryVariables: variables.map((variable) => ({key: variable, value: ''})),
+ });
+ }}
+ />
+ >
+ )}
+
+
+
+
+ {(editorState) => getVariableOptions(editorState)}
+
+
+ >
+ );
+}
const ExecuteQueryTmpAction = (function () {
const childrenMap = {
queryName: SimpleNameComp,
+ queryVariables: withDefault(keyValueListControl(false, [], "string"), [])
};
return new MultiCompBuilder(childrenMap, () => {
return () => Promise.resolve(undefined as unknown);
@@ -22,6 +112,15 @@ const ExecuteQueryTmpAction = (function () {
export class ExecuteQueryAction extends ExecuteQueryTmpAction {
override getView() {
const queryName = this.children.queryName.getView();
+ // const queryParams = keyValueListToSearchStr(Array.isArray(this?.children?.query) ? (this.children.query as unknown as any[]).map((i: any) => i.getView() as KeyValue) : []);
+ const result = Object.values(this.children.queryVariables.children as Record)
+ .filter(item => item.children.key.unevaledValue !== "" && item.children.value.unevaledValue !== "")
+ .map(item => ({[item.children.key.unevaledValue]: item.children.value.unevaledValue}))
+ .reduce((acc, curr) => Object.assign(acc, curr), {});
if (!queryName) {
return () => Promise.resolve();
}
@@ -30,9 +129,7 @@ export class ExecuteQueryAction extends ExecuteQueryTmpAction {
this.dispatch,
routeByNameAction(
queryName,
- executeQueryAction({
- // can add context in the future
- })
+ executeQueryAction({args: result})
),
{ notHandledError: trans("eventHandler.notHandledError") }
);
@@ -46,55 +143,11 @@ export class ExecuteQueryAction extends ExecuteQueryTmpAction {
}
propertyView({ placement }: { placement?: "query" | "table" }) {
- const getQueryOptions = (editorState?: EditorState) => {
- const options: { label: string; value: string }[] =
- editorState
- ?.queryCompInfoList()
- .map((info) => ({
- label: info.name,
- value: info.name,
- }))
- .filter(
- // Filter out the current query under query
- (option) => {
- if (
- placement === "query" &&
- editorState.selectedBottomResType === BottomResTypeEnum.Query
- ) {
- return option.value !== editorState.selectedBottomResName;
- }
- return true;
- }
- ) || [];
-
- // input queries
- editorState
- ?.getModuleLayoutComp()
- ?.getInputs()
- .forEach((i) => {
- const { name, type } = i.getView();
- if (type === InputTypeEnum.Query) {
- options.push({ label: name, value: name });
- }
- });
- return options;
- };
return (
-
-
- {(editorState) => (
- <>
- this.dispatchChangeValueAction({ queryName: value })}
- />
- >
- )}
-
-
- );
+
+ )
}
}
diff --git a/client/packages/lowcoder/src/comps/controls/actionSelector/goToURLAction.tsx b/client/packages/lowcoder/src/comps/controls/actionSelector/goToURLAction.tsx
index 462cd6213..2223079ef 100644
--- a/client/packages/lowcoder/src/comps/controls/actionSelector/goToURLAction.tsx
+++ b/client/packages/lowcoder/src/comps/controls/actionSelector/goToURLAction.tsx
@@ -20,7 +20,7 @@ const childrenMap = {
};
export const GoToURLAction = new MultiCompBuilder(childrenMap, (props) => {
- return () => {
+ return () => {
const queryParams = keyValueListToSearchStr(
props.query.map((i) => i.getView() as KeyValue)
);
diff --git a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
index 3a22afe71..70bf77962 100644
--- a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx
@@ -167,12 +167,18 @@ const EventHandlerControlPropertyView = (props: {
if (eventConfigs.length === 0) {
return;
}
+
+ const queryVariables = editorState
+ ?.selectedOrFirstQueryComp()
+ ?.children.variables.children.variables.toJsonValue();
+
const queryExecHandler = {
compType: "executeQuery",
comp: {
queryName: editorState
?.selectedOrFirstQueryComp()
?.children.name.getView(),
+ queryVariables: queryVariables?.map((variable) => ({...variable, value: ''})),
},
};
const messageHandler = {
diff --git a/client/packages/lowcoder/src/comps/controls/keyValueControl.tsx b/client/packages/lowcoder/src/comps/controls/keyValueControl.tsx
index 89cc6d276..1f4d722a2 100644
--- a/client/packages/lowcoder/src/comps/controls/keyValueControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/keyValueControl.tsx
@@ -47,6 +47,8 @@ export type KeyValueControlParams = ControlParams & {
typeTooltip?: ReactNode;
keyFlexBasics?: number;
valueFlexBasics?: number;
+ isStatic?: boolean;
+ keyFixed?: boolean;
};
/**
@@ -82,16 +84,20 @@ function keyValueControl(
return (
- {this.children.key.propertyView({ placeholder: "key", indentWithTab: false })}
- {hasType && params.showType && (
-
- {this.children.type.propertyView({
- placeholder: "key",
- indentWithTab: false,
- tooltip: params.typeTooltip,
- })}
-
- )}
+ {params.keyFixed?
+ <>{this.children.key.getView()}>
+ :<>
+ {this.children.key.propertyView({ placeholder: "key", indentWithTab: false })}
+ {hasType && params.showType && (
+
+ {this.children.type.propertyView({
+ placeholder: "key",
+ indentWithTab: false,
+ tooltip: params.typeTooltip,
+ })}
+
+ )}
+ >}
{this.children.value.propertyView({
@@ -136,6 +142,7 @@ export function keyValueListControl(
list={this.getView().map((child) => child.propertyView(params))}
onAdd={() => this.dispatch(this.pushAction({}))}
onDelete={(item, index) => this.dispatch(this.deleteAction(index))}
+ isStatic={params.isStatic}
/>
);
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
index e90bf8d19..fbfabeaaa 100644
--- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
@@ -67,7 +67,7 @@ import { JSONObject, JSONValue } from "../../util/jsonTypes";
import { BoolPureControl } from "../controls/boolControl";
import { millisecondsControl } from "../controls/millisecondControl";
import { paramsMillisecondsControl } from "../controls/paramsControl";
-import { NameConfig, withExposingConfigs } from "../generators/withExposing";
+import { DepsConfig, NameConfig, withExposingConfigs } from "../generators/withExposing";
import { HttpQuery } from "./httpQuery/httpQuery";
import { StreamQuery } from "./httpQuery/streamQuery";
import { QueryConfirmationModal } from "./queryComp/queryConfirmationModal";
@@ -75,6 +75,7 @@ import { QueryNotificationControl } from "./queryComp/queryNotificationControl";
import { QueryPropertyView } from "./queryComp/queryPropertyView";
import { getTriggerType, onlyManualTrigger } from "./queryCompUtils";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
+import {VariablesComp} from "@lowcoder-ee/comps/queries/queryComp/variablesComp";
const latestExecution: Record = {};
@@ -153,6 +154,7 @@ const childrenMap = {
defaultValue: 10 * 1000,
}),
confirmationModal: QueryConfirmationModal,
+ variables: VariablesComp,
periodic: BoolPureControl,
periodicTime: millisecondsControl({
defaultValue: Number.NaN,
@@ -361,6 +363,8 @@ QueryCompTmp = class extends QueryCompTmp {
}
if (action.type === CompActionTypes.EXECUTE_QUERY) {
if (getReduceContext().disableUpdateState) return this;
+ if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {});
+
return this.executeQuery(action);
}
if (action.type === CompActionTypes.CHANGE_VALUE) {
@@ -404,16 +408,21 @@ QueryCompTmp = class extends QueryCompTmp {
return this;
}
+
+
+
/**
* Process the execution result
*/
private processResult(result: QueryResult, action: ExecuteQueryAction, startTime: number) {
const lastQueryStartTime = this.children.lastQueryStartTime.getView();
+
if (lastQueryStartTime > startTime) {
// There are more new requests, ignore this result
// FIXME: cancel this request in advance in the future
return;
}
+
const changeAction = multiChangeAction({
code: this.children.code.changeValueAction(result.code ?? QUERY_EXECUTION_OK),
success: this.children.success.changeValueAction(result.success ?? true),
@@ -470,6 +479,7 @@ QueryCompTmp = class extends QueryCompTmp {
applicationId: applicationId,
applicationPath: parentApplicationPath,
args: action.args,
+ variables: action.args,
timeout: this.children.timeout,
callback: (result) => this.processResult(result, action, startTime)
});
@@ -653,6 +663,23 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [
new NameConfig("isFetching", trans("query.isFetchingExportDesc")),
new NameConfig("runTime", trans("query.runTimeExportDesc")),
new NameConfig("latestEndTime", trans("query.latestEndTimeExportDesc")),
+ new DepsConfig(
+ "variable",
+ (children: any) => {
+ return {data: children.variables.children.variables.node()};
+ },
+ (input) => {
+ if (!input.data) {
+ return undefined;
+ }
+ const newNode = Object.values(input.data)
+ .filter((kvNode: any) => kvNode.key.text.value)
+ .map((kvNode: any) => ({[kvNode.key.text.value]: kvNode.value.text.value}))
+ .reduce((prev, obj) => ({...prev, ...obj}), {});
+ return newNode;
+ },
+ trans("query.variables")
+ ),
new NameConfig("triggerType", trans("query.triggerTypeExportDesc")),
]);
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx
index 07f4ef1e0..fa0f078e1 100644
--- a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx
@@ -171,6 +171,17 @@ export function QueryPropertyView(props: { comp: InstanceType
),
},
+ {
+ key: "variables",
+ title: trans("query.variablesTab"),
+ children: (
+
+
+ {children.variables.getPropertyView()}
+
+
+ ),
+ },
] as const
}
tabTitle={children.name.getView()}
@@ -501,6 +512,29 @@ export const QueryGeneralPropertyView = (props: {
);
};
+export const QueryVariablesPropertyView = (props: {
+ comp: InstanceType;
+ placement?: PageType;
+}) => {
+ const { comp, placement = "editor" } = props;
+
+ const children = comp.children;
+ let datasourceId = children.datasourceId.getView();
+
+ console.log(children.datasourceId);
+ return (
+
+
+ {isCompWithPropertyView(children.comp)
+ ? children.comp.propertyView({
+ datasourceId: datasourceId,
+ })
+ : children.comp.getPropertyView()}
+
+
+ );
+};
+
function findQueryInNestedStructure(
structure: any,
queryName: string,
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx
new file mode 100644
index 000000000..87c5bd432
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/queries/queryComp/variablesComp.tsx
@@ -0,0 +1,16 @@
+import {MultiCompBuilder, withDefault} from "../../generators";
+import {keyValueListControl} from "lowcoder-sdk";
+
+export const VariablesComp = new MultiCompBuilder(
+ {
+ variables: withDefault(keyValueListControl(), [{ key: "", value: "" }]),
+ },
+ (props) =>
+ props.variables
+ )
+ .setPropertyViewFn((children) => (
+ <>
+ {children.variables.propertyView({})}
+ >
+ ))
+ .build();
diff --git a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
index 1971e8ec5..1203e1cfd 100644
--- a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
@@ -25,18 +25,23 @@ export function toQueryView(params: FunctionProperty[]) {
applicationId: string;
applicationPath: string[];
args?: Record;
+ variables?: any;
timeout: InstanceType;
}): Promise => {
const { applicationId, isViewMode } = getGlobalSettings();
+ const mappedVariables = Object.keys(props.variables).map(key => ({key: `query1.variable.${key}`, value: props.variables[key]}));
let request: QueryExecuteRequest = {
path: props.applicationPath,
params: [
- ...params.map(({ key, value }) => ({ key, value: value(props.args) })),
+ ...params.filter(param => {
+ return !mappedVariables.map(v => v.key).includes(param.key);
+ }).map(({ key, value }) => ({ key, value: value(props.args) })),
...Object.entries(props.timeout.getView()).map(([key, value]) => ({
key,
value: value(props.args),
})),
+ ...mappedVariables,
],
viewMode: !!isViewMode,
};
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 9c8cdddfa..7623177c6 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -277,6 +277,7 @@ export const en = {
"moduleEvent": "Module Event",
"goToApp": "Go to an other App",
"queryParams": "Query Parameters",
+ "queryVariables": "Query Variables",
"hashParams": "Hash Parameters",
"showNotification": "Show a Notification",
"text": "Text",
@@ -705,6 +706,7 @@ export const en = {
"newDatasource": "New Data Source",
"generalTab": "General",
"notificationTab": "Notification",
+ "variablesTab": "Variables",
"advancedTab": "Advanced",
"showFailNotification": "Show Notification on Failure",
"failCondition": "Failure Conditions",
diff --git a/client/packages/lowcoder/src/util/appUtils.tsx b/client/packages/lowcoder/src/util/appUtils.tsx
index 5aed643ae..be38dfc22 100644
--- a/client/packages/lowcoder/src/util/appUtils.tsx
+++ b/client/packages/lowcoder/src/util/appUtils.tsx
@@ -32,6 +32,7 @@ export function openApp(props: {
hashParams?: string;
newTab?: boolean;
}) {
+ console.log(props.queryParams)
const m = matchPath(window.location.pathname, APP_EDITOR_URL);
if (!m || !props.applicationId) {
return;