Skip to content

Commit 68d7c83

Browse files
authored
Merge pull request #1470 from lowcoder-org/feature-dataQuery
Added variables to data query
2 parents 8190bea + e43aba8 commit 68d7c83

File tree

11 files changed

+225
-69
lines changed

11 files changed

+225
-69
lines changed

client/packages/lowcoder-design/src/components/keyValueList.tsx

+9-4
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,25 @@ export const KeyValueList = (props: {
7676
list: ReactNode[];
7777
onAdd: () => void;
7878
onDelete: (item: ReactNode, index: number) => void;
79+
isStatic?: boolean;
7980
}) => (
8081
<>
8182
{props.list.map((item, index) => (
8283
<KeyValueListItem key={index /* FIXME: find a proper key instead of `index` */}>
8384
{item}
84-
<DelIcon
85-
onClick={() => props.list.length > 1 && props.onDelete(item, index)}
86-
$forbidden={props.list.length === 1}
87-
/>
85+
{!props.isStatic &&
86+
<DelIcon
87+
onClick={() => props.list.length > 1 && props.onDelete(item, index)}
88+
$forbidden={props.list.length === 1}
89+
/>
90+
}
8891
</KeyValueListItem>
8992
))}
93+
{!props.isStatic &&
9094
<AddBtn onClick={props.onAdd}>
9195
<AddIcon />
9296
{trans("addItem")}
9397
</AddBtn>
98+
}
9499
</>
95100
);

client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx

+105-52
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,100 @@ import { BranchDiv, Dropdown } from "lowcoder-design";
77
import { BottomResTypeEnum } from "types/bottomRes";
88
import { getPromiseAfterDispatch } from "util/promiseUtils";
99
import { trans } from "i18n";
10+
import {keyValueListControl, keyValueListToSearchStr, withDefault} from "lowcoder-sdk";
11+
import {KeyValue} from "@lowcoder-ee/types/common";
12+
import { useCallback, useContext, useEffect, useMemo } from "react";
1013

14+
const ExecuteQueryPropertyView = ({
15+
comp,
16+
placement,
17+
}: {
18+
comp: any,
19+
placement?: "query" | "table"
20+
}) => {
21+
const getQueryOptions = useCallback((editorState?: EditorState) => {
22+
const options: { label: string; value: string; variable?: Record<string, string> }[] =
23+
editorState
24+
?.queryCompInfoList()
25+
.map((info) => {
26+
return {
27+
label: info.name,
28+
value: info.name,
29+
variable: info.data.variable,
30+
}
31+
})
32+
.filter(
33+
// Filter out the current query under query
34+
(option) => {
35+
if (
36+
placement === "query" &&
37+
editorState.selectedBottomResType === BottomResTypeEnum.Query
38+
) {
39+
return option.value !== editorState.selectedBottomResName;
40+
}
41+
return true;
42+
}
43+
) || [];
44+
45+
// input queries
46+
editorState
47+
?.getModuleLayoutComp()
48+
?.getInputs()
49+
.forEach((i) => {
50+
const { name, type } = i.getView();
51+
if (type === InputTypeEnum.Query) {
52+
options.push({ label: name, value: name });
53+
}
54+
});
55+
return options;
56+
}, [placement]);
57+
58+
const getVariableOptions = useCallback((editorState?: EditorState) => {
59+
return comp.children.queryVariables.propertyView({
60+
label: trans("eventHandler.queryVariables"),
61+
layout: "vertical",
62+
isStatic: true,
63+
keyFixed: true,
64+
});
65+
}, [comp.children.queryVariables.getView()])
66+
67+
return (
68+
<>
69+
<BranchDiv $type={"inline"}>
70+
<EditorContext.Consumer>
71+
{(editorState) => (
72+
<>
73+
<Dropdown
74+
showSearch={true}
75+
value={comp.children.queryName.getView()}
76+
options={getQueryOptions(editorState)}
77+
label={trans("eventHandler.selectQuery")}
78+
onChange={(value) => {
79+
const options = getQueryOptions(editorState);
80+
const selectedQuery = options.find(option => option.value === value);
81+
const variables = selectedQuery ? Object.keys(selectedQuery.variable || {}) : [];
82+
comp.dispatchChangeValueAction({
83+
queryName: value,
84+
queryVariables: variables.map((variable) => ({key: variable, value: ''})),
85+
});
86+
}}
87+
/>
88+
</>
89+
)}
90+
</EditorContext.Consumer>
91+
</BranchDiv>
92+
<BranchDiv>
93+
<EditorContext.Consumer>
94+
{(editorState) => getVariableOptions(editorState)}
95+
</EditorContext.Consumer>
96+
</BranchDiv>
97+
</>
98+
);
99+
}
11100
const ExecuteQueryTmpAction = (function () {
12101
const childrenMap = {
13102
queryName: SimpleNameComp,
103+
queryVariables: withDefault(keyValueListControl(false, [], "string"), [])
14104
};
15105
return new MultiCompBuilder(childrenMap, () => {
16106
return () => Promise.resolve(undefined as unknown);
@@ -22,6 +112,15 @@ const ExecuteQueryTmpAction = (function () {
22112
export class ExecuteQueryAction extends ExecuteQueryTmpAction {
23113
override getView() {
24114
const queryName = this.children.queryName.getView();
115+
// const queryParams = keyValueListToSearchStr(Array.isArray(this?.children?.query) ? (this.children.query as unknown as any[]).map((i: any) => i.getView() as KeyValue) : []);
116+
const result = Object.values(this.children.queryVariables.children as Record<string, {
117+
children: {
118+
key: { unevaledValue: string },
119+
value: { unevaledValue: string }
120+
}}>)
121+
.filter(item => item.children.key.unevaledValue !== "" && item.children.value.unevaledValue !== "")
122+
.map(item => ({[item.children.key.unevaledValue]: item.children.value.unevaledValue}))
123+
.reduce((acc, curr) => Object.assign(acc, curr), {});
25124
if (!queryName) {
26125
return () => Promise.resolve();
27126
}
@@ -30,9 +129,7 @@ export class ExecuteQueryAction extends ExecuteQueryTmpAction {
30129
this.dispatch,
31130
routeByNameAction(
32131
queryName,
33-
executeQueryAction({
34-
// can add context in the future
35-
})
132+
executeQueryAction({args: result})
36133
),
37134
{ notHandledError: trans("eventHandler.notHandledError") }
38135
);
@@ -46,55 +143,11 @@ export class ExecuteQueryAction extends ExecuteQueryTmpAction {
46143
}
47144

48145
propertyView({ placement }: { placement?: "query" | "table" }) {
49-
const getQueryOptions = (editorState?: EditorState) => {
50-
const options: { label: string; value: string }[] =
51-
editorState
52-
?.queryCompInfoList()
53-
.map((info) => ({
54-
label: info.name,
55-
value: info.name,
56-
}))
57-
.filter(
58-
// Filter out the current query under query
59-
(option) => {
60-
if (
61-
placement === "query" &&
62-
editorState.selectedBottomResType === BottomResTypeEnum.Query
63-
) {
64-
return option.value !== editorState.selectedBottomResName;
65-
}
66-
return true;
67-
}
68-
) || [];
69-
70-
// input queries
71-
editorState
72-
?.getModuleLayoutComp()
73-
?.getInputs()
74-
.forEach((i) => {
75-
const { name, type } = i.getView();
76-
if (type === InputTypeEnum.Query) {
77-
options.push({ label: name, value: name });
78-
}
79-
});
80-
return options;
81-
};
82146
return (
83-
<BranchDiv $type={"inline"}>
84-
<EditorContext.Consumer>
85-
{(editorState) => (
86-
<>
87-
<Dropdown
88-
showSearch={true}
89-
value={this.children.queryName.getView()}
90-
options={getQueryOptions(editorState)}
91-
label={trans("eventHandler.selectQuery")}
92-
onChange={(value) => this.dispatchChangeValueAction({ queryName: value })}
93-
/>
94-
</>
95-
)}
96-
</EditorContext.Consumer>
97-
</BranchDiv>
98-
);
147+
<ExecuteQueryPropertyView
148+
comp={this}
149+
placement={placement}
150+
/>
151+
)
99152
}
100153
}

client/packages/lowcoder/src/comps/controls/actionSelector/goToURLAction.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const childrenMap = {
2020
};
2121

2222
export const GoToURLAction = new MultiCompBuilder(childrenMap, (props) => {
23-
return () => {
23+
return () => {
2424
const queryParams = keyValueListToSearchStr(
2525
props.query.map((i) => i.getView() as KeyValue)
2626
);

client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,18 @@ const EventHandlerControlPropertyView = (props: {
167167
if (eventConfigs.length === 0) {
168168
return;
169169
}
170+
171+
const queryVariables = editorState
172+
?.selectedOrFirstQueryComp()
173+
?.children.variables.children.variables.toJsonValue();
174+
170175
const queryExecHandler = {
171176
compType: "executeQuery",
172177
comp: {
173178
queryName: editorState
174179
?.selectedOrFirstQueryComp()
175180
?.children.name.getView(),
181+
queryVariables: queryVariables?.map((variable) => ({...variable, value: ''})),
176182
},
177183
};
178184
const messageHandler = {

client/packages/lowcoder/src/comps/controls/keyValueControl.tsx

+17-10
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export type KeyValueControlParams = ControlParams & {
4747
typeTooltip?: ReactNode;
4848
keyFlexBasics?: number;
4949
valueFlexBasics?: number;
50+
isStatic?: boolean;
51+
keyFixed?: boolean;
5052
};
5153

5254
/**
@@ -82,16 +84,20 @@ function keyValueControl<T extends OptionsType>(
8284
return (
8385
<KeyValueWrapper>
8486
<KeyWrapper $flexBasics={params.keyFlexBasics}>
85-
{this.children.key.propertyView({ placeholder: "key", indentWithTab: false })}
86-
{hasType && params.showType && (
87-
<TypeWrapper>
88-
{this.children.type.propertyView({
89-
placeholder: "key",
90-
indentWithTab: false,
91-
tooltip: params.typeTooltip,
92-
})}
93-
</TypeWrapper>
94-
)}
87+
{params.keyFixed?
88+
<>{this.children.key.getView()}</>
89+
:<>
90+
{this.children.key.propertyView({ placeholder: "key", indentWithTab: false })}
91+
{hasType && params.showType && (
92+
<TypeWrapper>
93+
{this.children.type.propertyView({
94+
placeholder: "key",
95+
indentWithTab: false,
96+
tooltip: params.typeTooltip,
97+
})}
98+
</TypeWrapper>
99+
)}
100+
</>}
95101
</KeyWrapper>
96102
<ValueWrapper $flexBasics={params.valueFlexBasics}>
97103
{this.children.value.propertyView({
@@ -136,6 +142,7 @@ export function keyValueListControl<T extends OptionsType>(
136142
list={this.getView().map((child) => child.propertyView(params))}
137143
onAdd={() => this.dispatch(this.pushAction({}))}
138144
onDelete={(item, index) => this.dispatch(this.deleteAction(index))}
145+
isStatic={params.isStatic}
139146
/>
140147
</ControlPropertyViewWrapper>
141148
);

client/packages/lowcoder/src/comps/queries/queryComp.tsx

+28-1
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,15 @@ import { JSONObject, JSONValue } from "../../util/jsonTypes";
6767
import { BoolPureControl } from "../controls/boolControl";
6868
import { millisecondsControl } from "../controls/millisecondControl";
6969
import { paramsMillisecondsControl } from "../controls/paramsControl";
70-
import { NameConfig, withExposingConfigs } from "../generators/withExposing";
70+
import { DepsConfig, NameConfig, withExposingConfigs } from "../generators/withExposing";
7171
import { HttpQuery } from "./httpQuery/httpQuery";
7272
import { StreamQuery } from "./httpQuery/streamQuery";
7373
import { QueryConfirmationModal } from "./queryComp/queryConfirmationModal";
7474
import { QueryNotificationControl } from "./queryComp/queryNotificationControl";
7575
import { QueryPropertyView } from "./queryComp/queryPropertyView";
7676
import { getTriggerType, onlyManualTrigger } from "./queryCompUtils";
7777
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
78+
import {VariablesComp} from "@lowcoder-ee/comps/queries/queryComp/variablesComp";
7879

7980
const latestExecution: Record<string, string> = {};
8081

@@ -153,6 +154,7 @@ const childrenMap = {
153154
defaultValue: 10 * 1000,
154155
}),
155156
confirmationModal: QueryConfirmationModal,
157+
variables: VariablesComp,
156158
periodic: BoolPureControl,
157159
periodicTime: millisecondsControl({
158160
defaultValue: Number.NaN,
@@ -361,6 +363,8 @@ QueryCompTmp = class extends QueryCompTmp {
361363
}
362364
if (action.type === CompActionTypes.EXECUTE_QUERY) {
363365
if (getReduceContext().disableUpdateState) return this;
366+
if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {});
367+
364368
return this.executeQuery(action);
365369
}
366370
if (action.type === CompActionTypes.CHANGE_VALUE) {
@@ -404,16 +408,21 @@ QueryCompTmp = class extends QueryCompTmp {
404408
return this;
405409
}
406410

411+
412+
413+
407414
/**
408415
* Process the execution result
409416
*/
410417
private processResult(result: QueryResult, action: ExecuteQueryAction, startTime: number) {
411418
const lastQueryStartTime = this.children.lastQueryStartTime.getView();
419+
412420
if (lastQueryStartTime > startTime) {
413421
// There are more new requests, ignore this result
414422
// FIXME: cancel this request in advance in the future
415423
return;
416424
}
425+
417426
const changeAction = multiChangeAction({
418427
code: this.children.code.changeValueAction(result.code ?? QUERY_EXECUTION_OK),
419428
success: this.children.success.changeValueAction(result.success ?? true),
@@ -470,6 +479,7 @@ QueryCompTmp = class extends QueryCompTmp {
470479
applicationId: applicationId,
471480
applicationPath: parentApplicationPath,
472481
args: action.args,
482+
variables: action.args,
473483
timeout: this.children.timeout,
474484
callback: (result) => this.processResult(result, action, startTime)
475485
});
@@ -653,6 +663,23 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [
653663
new NameConfig("isFetching", trans("query.isFetchingExportDesc")),
654664
new NameConfig("runTime", trans("query.runTimeExportDesc")),
655665
new NameConfig("latestEndTime", trans("query.latestEndTimeExportDesc")),
666+
new DepsConfig(
667+
"variable",
668+
(children: any) => {
669+
return {data: children.variables.children.variables.node()};
670+
},
671+
(input) => {
672+
if (!input.data) {
673+
return undefined;
674+
}
675+
const newNode = Object.values(input.data)
676+
.filter((kvNode: any) => kvNode.key.text.value)
677+
.map((kvNode: any) => ({[kvNode.key.text.value]: kvNode.value.text.value}))
678+
.reduce((prev, obj) => ({...prev, ...obj}), {});
679+
return newNode;
680+
},
681+
trans("query.variables")
682+
),
656683
new NameConfig("triggerType", trans("query.triggerTypeExportDesc")),
657684
]);
658685

0 commit comments

Comments
 (0)