From 1f233a331f6e7285dd532b9be467ff38852ff931 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Thu, 31 Aug 2023 18:17:07 +0500 Subject: [PATCH 1/5] feat: added map mode in echarts --- client/packages/lowcoder-comps/package.json | 3 +- .../src/comps/chartComp/chartComp.tsx | 52 +++++++++++++++--- .../chartComp/chartConfigs/chartUrls.tsx | 1 + .../src/comps/chartComp/chartConstants.tsx | 16 +++++- .../src/comps/chartComp/chartPropertyView.tsx | 55 ++++++++++++++++++- .../src/comps/chartComp/chartUtils.ts | 47 ++++++++++++++-- .../src/comps/chartComp/reactEcharts/core.tsx | 3 +- .../src/comps/chartComp/reactEcharts/index.ts | 4 +- .../src/comps/chartComp/reactEcharts/types.ts | 6 +- .../src/i18n/comps/locales/enObj.tsx | 22 ++++++++ .../src/i18n/comps/locales/types.tsx | 1 + client/yarn.lock | 8 +++ 12 files changed, 196 insertions(+), 22 deletions(-) diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json index 081b36f1d..264e38233 100644 --- a/client/packages/lowcoder-comps/package.json +++ b/client/packages/lowcoder-comps/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder-comps", - "version": "0.0.12", + "version": "0.0.13", "type": "module", "license": "MIT", "dependencies": { @@ -14,6 +14,7 @@ "@types/react": "17", "@types/react-dom": "17", "big.js": "^6.2.1", + "echarts-extension-gmap": "^1.6.0", "lowcoder-cli": "workspace:^", "lowcoder-sdk": "workspace:^", "mermaid": "^10.2.4", diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx index 5f94533a8..e0dcf03cc 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx @@ -26,6 +26,7 @@ import { withViewFn, ThemeContext, chartColorPalette, + loadScript, } from "lowcoder-sdk"; import { getEchartsLocale, trans } from "i18n/comps"; import { ItemColorComp } from "comps/chartComp/chartConfigs/lineChartConfig"; @@ -33,7 +34,9 @@ import { echartsConfigOmitChildren, getEchartsConfig, getSelectedPoints, + loadGoogleMapsScript, } from "comps/chartComp/chartUtils"; +import 'echarts-extension-gmap'; import log from "loglevel"; let ChartTmpComp = (function () { @@ -45,6 +48,7 @@ let ChartTmpComp = (function () { ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { const echartsCompRef = useRef(); const [chartSize, setChartSize] = useState(); + const [mapScriptLoaded, setMapScriptLoaded] = useState(false); const firstResize = useRef(true); const theme = useContext(ThemeContext); const defaultChartTheme = { @@ -87,6 +91,34 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { ); }, [chartSize, ...Object.values(echartsConfigChildren)]); + const isMapScriptLoaded = useMemo(() => { + return mapScriptLoaded || window?.google; + }, [mapScriptLoaded]) + + const loadGoogleMapsData = () => { + const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance(); + if (!echartsCompInstance) { + return _.noop; + } + echartsCompInstance.getModel().getComponent("gmap").getGoogleMap(); + } + + const apiKey = comp.children.mapApiKey.getView(); + const mode = comp.children.mode.getView(); + useEffect(() => { + if(mode === 'map') { + const gMapScript = loadGoogleMapsScript(''); + if(isMapScriptLoaded) { + loadGoogleMapsData(); + return; + } + gMapScript.addEventListener('load', function () { + setMapScriptLoaded(true); + loadGoogleMapsData(); + }); + } + }, [mode, apiKey, option]) + return ( { @@ -101,15 +133,17 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { } }} > - (echartsCompRef.current = e)} - style={{ height: "100%" }} - notMerge - lazyUpdate - opts={{ locale: getEchartsLocale() }} - option={option} - theme={themeConfig} - /> + {(mode !== 'map' || (mode === 'map' && isMapScriptLoaded)) && ( + (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + /> + )} ); }); diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx index a92e5c06a..355baf35f 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx @@ -4,3 +4,4 @@ const echartsUrlLocale = language === "zh" ? "zh" : "en"; export const optionUrl = `https://echarts.apache.org/${echartsUrlLocale}/option.html`; export const examplesUrl = `https://echarts.apache.org/examples/${echartsUrlLocale}/index.html`; export const xAxisTypeUrl = `${optionUrl}#xAxis.type`; +export const googleMapsApiUrl = `https://maps.googleapis.com/maps/api/js`; \ No newline at end of file diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx index 52db1c5ab..fd284eb8a 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx @@ -1,5 +1,5 @@ import { jsonControl, JSONObject, stateComp, toJSONObjectArray, toObject } from "lowcoder-sdk"; -import { StringControl } from "lowcoder-sdk"; +import { withDefault, BooleanControl, StringControl, NumberControl, JSONObjectControl } from "lowcoder-sdk"; import { dropdownControl } from "lowcoder-sdk"; import { eventHandlerControl } from "lowcoder-sdk"; import { valueComp, withType } from "lowcoder-sdk"; @@ -15,7 +15,6 @@ import { ScatterChartConfig } from "./chartConfigs/scatterChartConfig"; import { SeriesListComp } from "./seriesComp"; import { EChartsOption } from "echarts"; import { i18nObjs, trans } from "i18n/comps"; -import { JSONValue } from "lowcoder"; export const ChartTypeOptions = [ { @@ -45,6 +44,10 @@ const chartModeOptions = [ label: "ECharts JSON", value: "json", }, + { + label: "Map", + value: "map", + }, ] as const; export const EventOptions = [ @@ -221,6 +224,14 @@ export const chartUiModeChildren = { chartConfig: ChartOptionComp, }; +const chartMapModeChildren = { + mapApiKey: withDefault(StringControl, ''), + mapZoomLevel: withDefault(NumberControl, 4), + mapCenterLng: withDefault(NumberControl, 120), + mapCenterLat: withDefault(NumberControl, 30), + mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption), +} + export const chartChildrenMap = { mode: dropdownControl(chartModeOptions, "ui"), echartsOption: jsonControl(toObject, i18nObjs.defaultEchartsJsonOption), @@ -236,6 +247,7 @@ export const chartChildrenMap = { }> >([]), ...chartUiModeChildren, + ...chartMapModeChildren, }; const chartUiChildrenMap = uiChildren(chartChildrenMap); diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx index 113c71c13..cda389a82 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx @@ -9,6 +9,7 @@ import { RedButton, Section, sectionNames, + controlItem, } from "lowcoder-sdk"; import { trans } from "i18n/comps"; import { examplesUrl, optionUrl } from "./chartConfigs/chartUrls"; @@ -147,6 +148,58 @@ export function chartPropertyView( ); + const mapModePropertyView = ( + <> +
+ {children.mapApiKey.propertyView({ + label: "API Key" + })} + {children.mapZoomLevel.propertyView({ + label: "Zoom Level" + })} + {controlItem({}, ( + + {'Center Position'} + + ))} + {children.mapCenterLng.propertyView({ + label: "Longitude" + })} + {children.mapCenterLat.propertyView({ + label: "Latitude" + })} +
+
+ {children.mapOptions.propertyView({ + label: trans("chart.echartsOptionLabel"), + styleName: "higher", + tooltip: ( +
+ + {trans("chart.echartsOptionTooltip")} + +
+ + {trans("chart.echartsOptionExamples")} + +
+ ), + })} +
+
{hiddenPropertyView(children)}
+ + ); + + const getChatConfigByMode = (mode: string) => { + switch(mode) { + case "ui": + return uiModePropertyView; + case "json": + return jsonModePropertyView; + case "map": + return mapModePropertyView; + } + } return ( <>
@@ -155,7 +208,7 @@ export function chartPropertyView( radioButton: true, })}
- {children.mode.getView() === "ui" ? uiModePropertyView : jsonModePropertyView} + {getChatConfigByMode(children.mode.getView())} ); } diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartUtils.ts b/client/packages/lowcoder-comps/src/comps/chartComp/chartUtils.ts index 280992714..14db6b7b8 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartUtils.ts +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartUtils.ts @@ -6,11 +6,12 @@ import { noDataPieChartConfig, } from "comps/chartComp/chartConstants"; import { getPieRadiusAndCenter } from "comps/chartComp/chartConfigs/pieChartConfig"; -import { EChartsOption } from "echarts"; +import { EChartsOptionWithMap } from "./reactEcharts/types"; import _ from "lodash"; -import { chartColorPalette, isNumeric, JSONObject } from "lowcoder-sdk"; +import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk"; import { calcXYConfig } from "comps/chartComp/chartConfigs/cartesianAxisConfig"; import Big from "big.js"; +import { googleMapsApiUrl } from "./chartConfigs/chartUrls"; export function transformData( originData: JSONObject[], @@ -122,10 +123,30 @@ export function getSeriesConfig(props: EchartsConfigProps) { } // https://echarts.apache.org/en/option.html -export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOption { +export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSize): EChartsOptionWithMap { if (props.mode === "json") { return props.echartsOption ? props.echartsOption : {}; } + if(props.mode === "map") { + const { + mapZoomLevel, + mapCenterLat, + mapCenterLng, + mapOptions, + } = props; + + const echartsOption = mapOptions ? mapOptions : {}; + return { + gmap: { + center: [mapCenterLng, mapCenterLat], + zoom: mapZoomLevel, + renderOnMoving: true, + // echartsLayerZIndex: 2019, + roam: true + }, + ...echartsOption, + } + } // axisChart const axisChart = isAxisChart(props.chartConfig.type); const gridPos = { @@ -134,7 +155,7 @@ export function getEchartsConfig(props: EchartsConfigProps, chartSize?: ChartSiz top: 50, bottom: 35, }; - let config: EChartsOption = { + let config: EChartsOptionWithMap = { title: { text: props.title, left: "center" }, tooltip: { confine: true, @@ -238,3 +259,21 @@ export function getSelectedPoints(param: any, option: any) { } return []; } + +export function loadGoogleMapsScript(apiKey?: string) { + const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`; + const scripts = document.getElementsByTagName('script'); + const scriptIndex = _.findIndex(scripts, (script) => script.src === mapsUrl); + if(scriptIndex > -1) { + return scripts[scriptIndex]; + } + + const script = document.createElement("script"); + script.type = "text/javascript"; + script.src = mapsUrl; + script.async = true; + script.defer = true; + window.document.body.appendChild(script); + + return script; +} diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/core.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/core.tsx index dd303e5fb..c90bd5b3d 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/core.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/core.tsx @@ -58,7 +58,8 @@ export default class EChartsReactCore extends PureComponent { if ( !isEqual(prevProps.theme, this.props.theme) || !isEqual(prevProps.opts, this.props.opts) || - !isEqual(prevProps.onEvents, this.props.onEvents) + !isEqual(prevProps.onEvents, this.props.onEvents) || + this.props.option.gmap ) { this.dispose(); diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/index.ts b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/index.ts index 80fc9ae9e..6be260645 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/index.ts +++ b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/index.ts @@ -1,5 +1,5 @@ import * as echarts from "echarts"; -import { EChartsReactProps, EChartsOption, EChartsInstance } from "./types"; +import { EChartsReactProps, EChartsInstance, EChartsOptionWithMap } from "./types"; import EChartsReactCore from "./core"; /** @@ -7,7 +7,7 @@ import EChartsReactCore from "./core"; * add exception-catch for setOption * if query isn't successfully loaded, chart will fail to load and can't reload */ -export type { EChartsReactProps, EChartsOption, EChartsInstance }; +export type { EChartsReactProps, EChartsOptionWithMap, EChartsInstance }; // export the Component the echarts Object. export default class EChartsReact extends EChartsReactCore { diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/types.ts b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/types.ts index c9f1ec973..b686408a4 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/types.ts +++ b/client/packages/lowcoder-comps/src/comps/chartComp/reactEcharts/types.ts @@ -1,6 +1,8 @@ import { CSSProperties } from "react"; +import { EChartsOption } from "echarts"; +import { GoogleMapComponentOption } from "echarts-extension-gmap"; -export type EChartsOption = any; +export type EChartsOptionWithMap = EChartsOption & GoogleMapComponentOption; export type EChartsInstance = any; @@ -28,7 +30,7 @@ export type EChartsReactProps = { /** * echarts option */ - readonly option: EChartsOption; + readonly option: EChartsOptionWithMap; /** * echarts theme config, can be: * 1. theme name string diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx index c96adaba1..f78c9b01e 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx @@ -1,6 +1,26 @@ import { I18nObjects } from "./types"; import { chartColorPalette } from "lowcoder-sdk"; + +const defaultMapData = { + tooltip: { + trigger: "item" + }, + animation: true, + series: [ + { + type: 'scatter', + coordinateSystem: 'gmap', + data: [[120, 30, 8], [120.1, 30.2, 20]], + encode: { + value: 2, + lng: 0, + lat: 1 + } + } + ] +} + export const enObj: I18nObjects = { defaultDataSource: [ { @@ -117,4 +137,6 @@ export const enObj: I18nObjects = { }, ], }, + + defaultMapJsonOption: defaultMapData, }; diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx b/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx index 821f631c6..92f3cc4b6 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx @@ -4,6 +4,7 @@ import { XAXisComponentOption } from "echarts"; export type I18nObjects = { defaultDataSource: JSONObject[]; defaultEchartsJsonOption: Record; + defaultMapJsonOption: Record; timeXAxisLabel?: XAXisComponentOption["axisLabel"]; imageEditorLocale?: Record; }; diff --git a/client/yarn.lock b/client/yarn.lock index c000939f6..4748d1eaf 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -7553,6 +7553,13 @@ __metadata: languageName: node linkType: hard +"echarts-extension-gmap@npm:^1.6.0": + version: 1.6.0 + resolution: "echarts-extension-gmap@npm:1.6.0" + checksum: 5bab07c7e739b81cf9c9c91b2d2db1101bab1f3b6bf649d6ebdf93fb4be0f9089058f934e4300a21a05dfb0cc0702214c48a5ec5fb34d7be19e419efbc317a61 + languageName: node + linkType: hard + "echarts@npm:^5.4.2": version: 5.4.2 resolution: "echarts@npm:5.4.2" @@ -11558,6 +11565,7 @@ __metadata: "@types/react": 17 "@types/react-dom": 17 big.js: ^6.2.1 + echarts-extension-gmap: ^1.6.0 jest: 29.3.0 lowcoder-cli: "workspace:^" lowcoder-sdk: "workspace:^" From 114fb380cc506a25f20bf97681e71fe8915e8d96 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Sun, 10 Sep 2023 15:33:08 +0500 Subject: [PATCH 2/5] feat: added stream query for real time data updates --- .../src/components/ResCreatePanel.tsx | 8 + .../queries/httpQuery/httpQueryConstants.tsx | 3 +- .../comps/queries/httpQuery/streamQuery.tsx | 276 ++++++++++++++++++ .../lowcoder/src/comps/queries/queryComp.tsx | 6 + .../queries/queryComp/queryPropertyView.tsx | 3 +- .../src/comps/queries/resourceDropdown.tsx | 16 + .../lowcoder/src/constants/queryConstants.ts | 3 + .../packages/lowcoder/src/i18n/locales/en.ts | 1 + .../packages/lowcoder/src/i18n/locales/zh.ts | 1 + 9 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx diff --git a/client/packages/lowcoder/src/components/ResCreatePanel.tsx b/client/packages/lowcoder/src/components/ResCreatePanel.tsx index 0b598ab7b..c5c85968a 100644 --- a/client/packages/lowcoder/src/components/ResCreatePanel.tsx +++ b/client/packages/lowcoder/src/components/ResCreatePanel.tsx @@ -161,6 +161,13 @@ const ResButton = (props: { dataSourceId: QUICK_REST_API_ID, }, }, + streamApi: { + label: trans("query.quickStreamAPI"), + type: BottomResTypeEnum.Query, + extra: { + compType: "streamApi", + }, + }, graphql: { label: trans("query.quickGraphql"), type: BottomResTypeEnum.Query, @@ -318,6 +325,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
+ {placement === "editor" && ( diff --git a/client/packages/lowcoder/src/comps/queries/httpQuery/httpQueryConstants.tsx b/client/packages/lowcoder/src/comps/queries/httpQuery/httpQueryConstants.tsx index f4b39272f..a0494477f 100644 --- a/client/packages/lowcoder/src/comps/queries/httpQuery/httpQueryConstants.tsx +++ b/client/packages/lowcoder/src/comps/queries/httpQuery/httpQueryConstants.tsx @@ -5,6 +5,7 @@ import { HttpQuery } from "./httpQuery"; import styled from "styled-components"; import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query"; import { GraphqlQuery } from "./graphqlQuery"; +import { StreamQuery } from "./streamQuery"; const UrlInput = styled.div<{ hasAddonBefore: boolean }>` display: flex; @@ -33,7 +34,7 @@ const UrlInputAddonBefore = styled.div` `; export const HttpPathPropertyView = (props: { - comp: InstanceType; + comp: InstanceType; datasourceId: string; urlPlaceholder?: string; }) => { diff --git a/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx b/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx new file mode 100644 index 000000000..1ef29faa9 --- /dev/null +++ b/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx @@ -0,0 +1,276 @@ +import { ControlPropertyViewWrapper } from "components/control"; +import { Input } from "components/Input"; +import { KeyValueList } from "components/keyValueList"; +import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query"; +import { simpleMultiComp } from "comps/generators/multi"; +import { ReactNode } from "react"; +import { JSONValue } from "../../../util/jsonTypes"; +import { keyValueListControl } from "../../controls/keyValueControl"; +import { ParamsJsonControl, ParamsStringControl } from "../../controls/paramsControl"; +import { list } from "../../generators/list"; +import { valueComp, withDefault } from "../../generators/simpleGenerators"; +import { FunctionProperty, toQueryView } from "../queryCompUtils"; +import { + HttpHeaderPropertyView, + HttpParametersPropertyView, + HttpPathPropertyView, +} from "./httpQueryConstants"; +import { QueryResult } from "../queryComp"; +import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "constants/queryConstants"; +import { FunctionControl } from "comps/controls/codeControl"; + +const connect = async (socket: WebSocket, timeout = 10000) => { + const isOpened = () => (socket.readyState === WebSocket.OPEN) + + if (socket.readyState !== WebSocket.CONNECTING) { + return isOpened() + } + else { + const intrasleep = 100 + const ttl = timeout / intrasleep // time to loop + let loop = 0 + while (socket.readyState === WebSocket.CONNECTING && loop < ttl) { + await new Promise(resolve => setTimeout(resolve, intrasleep)) + loop++ + } + return isOpened() + } +} + +const childrenMap = { + path: ParamsStringControl, + destroySocketConnection: FunctionControl, +}; + +const StreamTmpQuery = simpleMultiComp(childrenMap); + +export class StreamQuery extends StreamTmpQuery { + private socket: WebSocket | undefined; + + override getView() { + return async ( + p: { + args?: Record, + callback?: (result: QueryResult) => void + } + ): Promise => { + const children = this.children; + + try { + const timer = performance.now(); + this.socket = new WebSocket(children.path.children.text.getView()); + + this.socket.onopen = function(e) { + console.log("[open] Connection established"); + }; + + this.socket.onmessage = function(event) { + console.log(`[message] Data received from server: ${event.data}`); + if(typeof JSON.parse(event.data) === 'object') { + const result = { + data: JSON.parse(event.data), + code: QUERY_EXECUTION_OK, + success: true, + runTime: Number((performance.now() - timer).toFixed()), + } + p?.callback?.(result); + } + }; + + this.socket.onclose = function(event) { + if (event.wasClean) { + console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + } else { + console.log('[close] Connection died'); + } + }; + + this.socket.onerror = function(error) { + throw new Error(error as any) + }; + + const isConnectionOpen = await connect(this.socket); + if(!isConnectionOpen) { + return { + success: false, + data: "", + code: QUERY_EXECUTION_ERROR, + message: "Socket connection failed", + }; + } + + return { + data: "", + code: QUERY_EXECUTION_OK, + success: true, + runTime: Number((performance.now() - timer).toFixed()), + }; + } catch (e) { + return { + success: false, + data: "", + code: QUERY_EXECUTION_ERROR, + message: (e as any).message || "", + }; + } + }; + } + + propertyView(props: { datasourceId: string }) { + return ; + } + + destroy() { + this.socket?.close(); + } +} + +const PropertyView = (props: { comp: InstanceType; datasourceId: string }) => { + const { comp } = props; + + return ( + <> + + + ); +}; + + + +// import { ParamsStringControl } from "comps/controls/paramsControl"; +// import { FunctionControl, StringControl, codeControl } from "comps/controls/codeControl"; +// import { MultiCompBuilder } from "comps/generators"; +// import { QueryResult } from "../queryComp"; +// import { QueryTutorials } from "util/tutorialUtils"; +// import { DocLink } from "lowcoder-design"; +// import { getGlobalSettings } from "comps/utils/globalSettings"; +// import { trans } from "i18n"; +// import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "constants/queryConstants"; + +// const connect = async (socket: WebSocket, timeout = 10000) => { +// const isOpened = () => (socket.readyState === WebSocket.OPEN) + +// if (socket.readyState !== WebSocket.CONNECTING) { +// return isOpened() +// } +// else { +// const intrasleep = 100 +// const ttl = timeout / intrasleep // time to loop +// let loop = 0 +// while (socket.readyState === WebSocket.CONNECTING && loop < ttl) { +// await new Promise(resolve => setTimeout(resolve, intrasleep)) +// loop++ +// } +// return isOpened() +// } +// } + +// export const StreamQuery = (function () { +// const childrenMap = { +// path: StringControl, +// destroySocketConnection: FunctionControl, +// }; +// return new MultiCompBuilder(childrenMap, (props) => { +// const { orgCommonSettings } = getGlobalSettings(); +// const runInHost = !!orgCommonSettings?.runJavaScriptInHost; + +// console.log(props.path); +// return async ( +// p: { +// args?: Record, +// callback?: (result: QueryResult) => void +// } +// ): Promise => { +// console.log('Stream Query', props) + +// try { +// const timer = performance.now(); +// // const url = 'wss://free.blr2.piesocket.com/v3/1?api_key=yWUvGQggacrrTdXYjvTpRD5qhm4RIsglS7YJlKzp¬ify_self=1' +// const socket = new WebSocket(props.path); + +// props.destroySocketConnection = () => { +// socket.close(); +// }; + +// socket.onopen = function(e) { +// console.log("[open] Connection established"); +// }; + +// socket.onmessage = function(event) { +// console.log(`[message] Data received from server: ${event.data}`); +// console.log(JSON.parse(event.data)) +// if(typeof JSON.parse(event.data) === 'object') { +// const result = { +// data: JSON.parse(event.data), +// code: QUERY_EXECUTION_OK, +// success: true, +// runTime: Number((performance.now() - timer).toFixed()), +// } +// p?.callback?.(result); +// } +// }; + +// socket.onclose = function(event) { +// if (event.wasClean) { +// console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); +// } else { +// // e.g. server process killed or network down +// // event.code is usually 1006 in this case +// console.log('[close] Connection died'); +// } +// }; + +// socket.onerror = function(error) { +// throw new Error(error as any) +// }; +// const isConnectionOpen = await connect(socket); +// if(!isConnectionOpen) { +// return { +// success: false, +// data: "", +// code: QUERY_EXECUTION_ERROR, +// message: "Socket connection failed", +// }; +// } + +// // const data = await props.script(p.args, runInHost); +// return { +// data: "", +// code: QUERY_EXECUTION_OK, +// success: true, +// runTime: Number((performance.now() - timer).toFixed()), +// }; +// } catch (e) { +// return { +// success: false, +// data: "", +// code: QUERY_EXECUTION_ERROR, +// message: (e as any).message || "", +// }; +// } +// }; +// }) +// .setPropertyViewFn((children) => { +// return ( +// <> +// { +// children.path.propertyView({ +// label: "URL", +// placement: "bottom", +// placeholder:"wss://www.example.com/socketserver", +// }) +// } + +// {/* TODO: Add docs for Stream Query +// {QueryTutorials.js && ( +// {trans("query.jsQueryDocLink")} +// )} */} +// +// ); +// }) +// .build(); +// })(); diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx index 40c278f74..b3a483e2a 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx @@ -69,6 +69,7 @@ import { millisecondsControl } from "../controls/millisecondControl"; import { paramsMillisecondsControl } from "../controls/paramsControl"; import { NameConfig, withExposingConfigs } from "../generators/withExposing"; import { HttpQuery } from "./httpQuery/httpQuery"; +import { StreamQuery } from "./httpQuery/streamQuery"; import { QueryConfirmationModal } from "./queryComp/queryConfirmationModal"; import { QueryNotificationControl } from "./queryComp/queryNotificationControl"; import { QueryPropertyView } from "./queryComp/queryPropertyView"; @@ -419,6 +420,7 @@ QueryCompTmp = class extends QueryCompTmp { applicationPath: parentApplicationPath, args: action.args, timeout: this.children.timeout, + callback: (result) => this.processResult(result, action, startTime) }); }, getTriggerType(this) === "manual") .then( @@ -517,6 +519,7 @@ QueryCompTmp = class extends QueryCompTmp implements BottomResComp { switch (type) { case "js": case "restApi": + case "streamApi": case "mongodb": case "redis": case "es": @@ -708,6 +711,9 @@ class QueryListComp extends QueryListTmpComp implements BottomResListComp { ], }) ); + if(toDelQuery.children.compType.getView() === 'streamApi') { + (toDelQuery.children.comp as StreamQuery)?.destroy(); + } messageInstance.success(trans("query.deleteSuccessMessage", { undoKey })); } diff --git a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx index fbf5264c2..516c1aa7d 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx @@ -307,7 +307,7 @@ export const QueryGeneralPropertyView = (props: { [ { label: - children.compType.getView() === "js" + (children.compType.getView() === "js" || children.compType.getView() === "streamApi") ? trans("query.triggerTypePageLoad") : trans("query.triggerTypeAuto"), value: "automatic", @@ -363,6 +363,7 @@ function useDatasourceStatus(datasourceId: string, datasourceType: ResourceType) return useMemo(() => { if ( datasourceType === "js" || + datasourceType === "streamApi" || datasourceType === "libraryQuery" || datasourceId === QUICK_REST_API_ID || datasourceId === QUICK_GRAPHQL_ID || diff --git a/client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx b/client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx index 3adab66fa..d4b60a063 100644 --- a/client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx +++ b/client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx @@ -91,6 +91,11 @@ const QuickRestAPIValue: ResourceOptionValue = { type: "restApi", }; +const QuickStreamAPIValue: ResourceOptionValue = { + id: "", + type: "streamApi", +}; + const QuickGraphqlValue: ResourceOptionValue = { id: QUICK_GRAPHQL_ID, type: "graphql", @@ -254,6 +259,17 @@ export const ResourceDropdown = (props: ResourceDropdownProps) => { + + + {getBottomResIcon("restApi")} + {trans("query.quickStreamAPI")} + + + Date: Wed, 13 Sep 2023 17:28:38 +0500 Subject: [PATCH 3/5] refactor: stream query code --- .../comps/queries/httpQuery/streamQuery.tsx | 235 ++++-------------- 1 file changed, 44 insertions(+), 191 deletions(-) diff --git a/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx b/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx index 1ef29faa9..b643cc281 100644 --- a/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx +++ b/client/packages/lowcoder/src/comps/queries/httpQuery/streamQuery.tsx @@ -1,25 +1,14 @@ -import { ControlPropertyViewWrapper } from "components/control"; -import { Input } from "components/Input"; -import { KeyValueList } from "components/keyValueList"; -import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query"; import { simpleMultiComp } from "comps/generators/multi"; -import { ReactNode } from "react"; -import { JSONValue } from "../../../util/jsonTypes"; -import { keyValueListControl } from "../../controls/keyValueControl"; -import { ParamsJsonControl, ParamsStringControl } from "../../controls/paramsControl"; -import { list } from "../../generators/list"; -import { valueComp, withDefault } from "../../generators/simpleGenerators"; -import { FunctionProperty, toQueryView } from "../queryCompUtils"; +import { ParamsStringControl } from "../../controls/paramsControl"; import { - HttpHeaderPropertyView, - HttpParametersPropertyView, HttpPathPropertyView, } from "./httpQueryConstants"; import { QueryResult } from "../queryComp"; import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "constants/queryConstants"; import { FunctionControl } from "comps/controls/codeControl"; +import { JSONValue } from "util/jsonTypes"; -const connect = async (socket: WebSocket, timeout = 10000) => { +const socketConnection = async (socket: WebSocket, timeout = 10000) => { const isOpened = () => (socket.readyState === WebSocket.OPEN) if (socket.readyState !== WebSocket.CONNECTING) { @@ -37,6 +26,29 @@ const connect = async (socket: WebSocket, timeout = 10000) => { } } +const createSuccessResponse = ( + data: JSONValue, + runTime?: number, +): QueryResult => { + return { + data, + runTime, + success: true, + code: QUERY_EXECUTION_OK, + } +} + +const createErrorResponse = ( + message: string, +): QueryResult => { + return { + message, + data: "", + success: false, + code: QUERY_EXECUTION_ERROR, + } +} + const childrenMap = { path: ParamsStringControl, destroySocketConnection: FunctionControl, @@ -58,60 +70,37 @@ export class StreamQuery extends StreamTmpQuery { try { const timer = performance.now(); - this.socket = new WebSocket(children.path.children.text.getView()); - - this.socket.onopen = function(e) { - console.log("[open] Connection established"); - }; + const socketUrl = children.path.children.text.getView(); + + this.socket = new WebSocket(socketUrl); + this.socket.onopen = () => { + console.log("[WebSocket] Connection established"); + } - this.socket.onmessage = function(event) { - console.log(`[message] Data received from server: ${event.data}`); + this.socket.onmessage = (event) => { + console.log(`[WebSocket] Data received from server`); if(typeof JSON.parse(event.data) === 'object') { - const result = { - data: JSON.parse(event.data), - code: QUERY_EXECUTION_OK, - success: true, - runTime: Number((performance.now() - timer).toFixed()), - } + const result = createSuccessResponse(JSON.parse(event.data)) p?.callback?.(result); } - }; + } - this.socket.onclose = function(event) { - if (event.wasClean) { - console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); - } else { - console.log('[close] Connection died'); - } - }; + this.socket.onclose = () => { + console.log(`[WebSocket] Connection closed`); + } this.socket.onerror = function(error) { throw new Error(error as any) - }; + } - const isConnectionOpen = await connect(this.socket); + const isConnectionOpen = await socketConnection(this.socket); if(!isConnectionOpen) { - return { - success: false, - data: "", - code: QUERY_EXECUTION_ERROR, - message: "Socket connection failed", - }; + return createErrorResponse("Socket connection failed") } - return { - data: "", - code: QUERY_EXECUTION_OK, - success: true, - runTime: Number((performance.now() - timer).toFixed()), - }; + return createSuccessResponse("", Number((performance.now() - timer).toFixed())) } catch (e) { - return { - success: false, - data: "", - code: QUERY_EXECUTION_ERROR, - message: (e as any).message || "", - }; + return createErrorResponse((e as any).message || "") } }; } @@ -138,139 +127,3 @@ const PropertyView = (props: { comp: InstanceType; datasourc ); }; - - - -// import { ParamsStringControl } from "comps/controls/paramsControl"; -// import { FunctionControl, StringControl, codeControl } from "comps/controls/codeControl"; -// import { MultiCompBuilder } from "comps/generators"; -// import { QueryResult } from "../queryComp"; -// import { QueryTutorials } from "util/tutorialUtils"; -// import { DocLink } from "lowcoder-design"; -// import { getGlobalSettings } from "comps/utils/globalSettings"; -// import { trans } from "i18n"; -// import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "constants/queryConstants"; - -// const connect = async (socket: WebSocket, timeout = 10000) => { -// const isOpened = () => (socket.readyState === WebSocket.OPEN) - -// if (socket.readyState !== WebSocket.CONNECTING) { -// return isOpened() -// } -// else { -// const intrasleep = 100 -// const ttl = timeout / intrasleep // time to loop -// let loop = 0 -// while (socket.readyState === WebSocket.CONNECTING && loop < ttl) { -// await new Promise(resolve => setTimeout(resolve, intrasleep)) -// loop++ -// } -// return isOpened() -// } -// } - -// export const StreamQuery = (function () { -// const childrenMap = { -// path: StringControl, -// destroySocketConnection: FunctionControl, -// }; -// return new MultiCompBuilder(childrenMap, (props) => { -// const { orgCommonSettings } = getGlobalSettings(); -// const runInHost = !!orgCommonSettings?.runJavaScriptInHost; - -// console.log(props.path); -// return async ( -// p: { -// args?: Record, -// callback?: (result: QueryResult) => void -// } -// ): Promise => { -// console.log('Stream Query', props) - -// try { -// const timer = performance.now(); -// // const url = 'wss://free.blr2.piesocket.com/v3/1?api_key=yWUvGQggacrrTdXYjvTpRD5qhm4RIsglS7YJlKzp¬ify_self=1' -// const socket = new WebSocket(props.path); - -// props.destroySocketConnection = () => { -// socket.close(); -// }; - -// socket.onopen = function(e) { -// console.log("[open] Connection established"); -// }; - -// socket.onmessage = function(event) { -// console.log(`[message] Data received from server: ${event.data}`); -// console.log(JSON.parse(event.data)) -// if(typeof JSON.parse(event.data) === 'object') { -// const result = { -// data: JSON.parse(event.data), -// code: QUERY_EXECUTION_OK, -// success: true, -// runTime: Number((performance.now() - timer).toFixed()), -// } -// p?.callback?.(result); -// } -// }; - -// socket.onclose = function(event) { -// if (event.wasClean) { -// console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); -// } else { -// // e.g. server process killed or network down -// // event.code is usually 1006 in this case -// console.log('[close] Connection died'); -// } -// }; - -// socket.onerror = function(error) { -// throw new Error(error as any) -// }; -// const isConnectionOpen = await connect(socket); -// if(!isConnectionOpen) { -// return { -// success: false, -// data: "", -// code: QUERY_EXECUTION_ERROR, -// message: "Socket connection failed", -// }; -// } - -// // const data = await props.script(p.args, runInHost); -// return { -// data: "", -// code: QUERY_EXECUTION_OK, -// success: true, -// runTime: Number((performance.now() - timer).toFixed()), -// }; -// } catch (e) { -// return { -// success: false, -// data: "", -// code: QUERY_EXECUTION_ERROR, -// message: (e as any).message || "", -// }; -// } -// }; -// }) -// .setPropertyViewFn((children) => { -// return ( -// <> -// { -// children.path.propertyView({ -// label: "URL", -// placement: "bottom", -// placeholder:"wss://www.example.com/socketserver", -// }) -// } - -// {/* TODO: Add docs for Stream Query -// {QueryTutorials.js && ( -// {trans("query.jsQueryDocLink")} -// )} */} -// -// ); -// }) -// .build(); -// })(); From 1f41ce705395a97c3f793f66af643a9b1b44ee8d Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 13 Sep 2023 18:14:22 +0500 Subject: [PATCH 4/5] refactor: added more test data for map chart --- .../src/comps/chartComp/chartConstants.tsx | 6 +- .../src/i18n/comps/locales/enObj.tsx | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx index fd284eb8a..52e53b2b6 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartConstants.tsx @@ -226,9 +226,9 @@ export const chartUiModeChildren = { const chartMapModeChildren = { mapApiKey: withDefault(StringControl, ''), - mapZoomLevel: withDefault(NumberControl, 4), - mapCenterLng: withDefault(NumberControl, 120), - mapCenterLat: withDefault(NumberControl, 30), + mapZoomLevel: withDefault(NumberControl, 3), + mapCenterLng: withDefault(NumberControl, 15.932644), + mapCenterLat: withDefault(NumberControl, 50.942063), mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption), } diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx index f78c9b01e..6f82ef845 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx @@ -9,9 +9,65 @@ const defaultMapData = { animation: true, series: [ { + name: 'Population', type: 'scatter', coordinateSystem: 'gmap', - data: [[120, 30, 8], [120.1, 30.2, 20]], + itemStyle: { + color: "#00c1de" + }, + data: [ + { "name":"Azerbaijan","value":[47.395,40.43,8352021] }, + { "name":"Albania","value":[20.068,41.143,3153731] }, + { "name":"Armenia","value":[44.563,40.534,3017661] }, + { "name":"Bosnia and Herzegovina","value":[17.786,44.169,3915238] }, + { "name":"Bulgaria","value":[25.231,42.761,7744591] }, + { "name":"Cyprus","value":[33.219,35.043,836321] }, + { "name":"Denmark","value":[9.264,56.058,5416945] }, + { "name":"Ireland","value":[-8.152,53.177,4143294] }, + { "name":"Estonia","value":[25.793,58.674,1344312] }, + { "name":"Austria","value":[14.912,47.683,8291979] }, + { "name":"Czech Republic","value":[15.338,49.743,10191762] }, + { "name":"Finland","value":[26.272,64.504,5246004] }, + { "name":"France","value":[2.55,46.565,60990544] }, + { "name":"Georgia","value":[43.518,42.176,4473409] }, + { "name":"Germany","value":[9.851,51.11,82652369] }, + { "name":"Greece","value":[21.766,39.666,11099737] }, + { "name":"Croatia","value":[16.693,45.723,455149] }, + { "name":"Hungary","value":[19.134,47.07,10086387] }, + { "name":"Iceland","value":[-18.48,64.764,295732] }, + { "name":"Israel","value":[34.851,31.026,6692037] }, + { "name":"Italy","value":[12.8,42.7,5864636] }, + { "name":"Latvia","value":[25.641,56.858,2301793] }, + { "name":"Belarus","value":[28.047,53.54,9795287] }, + { "name":"Lithuania","value":[23.897,55.336,3425077] }, + { "name":"Slovakia","value":[19.491,48.707,5386995] }, + { "name":"Liechtenstein","value":[9.555,47.153,34598] }, + { "name":"The former Yugoslav Republic of Macedonia","value":[21.698,41.6,2033655] }, + { "name":"Malta","value":[14.442,35.89,402617] }, + { "name":"Belgium","value":[4.664,50.643,10398049] }, + { "name":"Faroe Islands","value":[-6.864,62.05,48205] }, + { "name":"Andorra","value":[1.576,42.549,73483] }, + { "name":"Luxembourg","value":[6.088,49.771,456613] }, + { "name":"Monaco","value":[7.412,43.75,325] }, + { "name":"Montenegro","value":[19.254,42.792,607969] }, + { "name":"Netherlands","value":[5.389,52.077,1632769] }, + { "name":"Norway","value":[8.74,61.152,4638836] }, + { "name":"Poland","value":[19.401,52.125,38195558] }, + { "name":"Portugal","value":[-8.058,40.309,10528226] }, + { "name":"Romania","value":[24.969,45.844,21627557] }, + { "name":"Republic of Moldova","value":[28.599,47.193,3876661] }, + { "name":"Slovenia","value":[14.827,46.124,1999425] }, + { "name":"Spain","value":[-3.649,40.227,43397491] }, + { "name":"Sweden","value":[15.27,62.011,9038049] }, + { "name":"Switzerland","value":[7.908,46.861,7424389] }, + { "name":"Turkey","value":[35.179,39.061,72969723] }, + { "name":"United Kingdom","value":[-1.6,53,60244834] }, + { "name":"Ukraine","value":[31.388,49.016,46917544] }, + { "name":"San Marino","value":[12.46,43.942,30214] }, + { "name":"Serbia","value":[20.806,44.032,9863026] }, + { "name":"Holy See (Vatican City)","value":[12.451,41.904,783] }, + { "name":"Russia","value":[96.689,61.988,143953092]} + ], encode: { value: 2, lng: 0, From b137622c8bb5bcc627b18b644a0ba3f40b20b584 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 13 Sep 2023 18:21:49 +0500 Subject: [PATCH 5/5] fix: updated map chart options,examples url --- .../src/comps/chartComp/chartConfigs/chartUrls.tsx | 4 +++- .../src/comps/chartComp/chartPropertyView.tsx | 10 +++++----- .../lowcoder-comps/src/i18n/comps/locales/en.ts | 2 ++ .../lowcoder-comps/src/i18n/comps/locales/zh.ts | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx index 355baf35f..ef8ada4b0 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/chartUrls.tsx @@ -4,4 +4,6 @@ const echartsUrlLocale = language === "zh" ? "zh" : "en"; export const optionUrl = `https://echarts.apache.org/${echartsUrlLocale}/option.html`; export const examplesUrl = `https://echarts.apache.org/examples/${echartsUrlLocale}/index.html`; export const xAxisTypeUrl = `${optionUrl}#xAxis.type`; -export const googleMapsApiUrl = `https://maps.googleapis.com/maps/api/js`; \ No newline at end of file +export const googleMapsApiUrl = `https://maps.googleapis.com/maps/api/js`; +export const mapOptionUrl = `https://github.com/plainheart/echarts-extension-gmap`; +export const mapExamplesUrl = `https://codepen.io/plainheart/pen/VweLGbR`; \ No newline at end of file diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx index cda389a82..0e114f1d3 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartPropertyView.tsx @@ -12,7 +12,7 @@ import { controlItem, } from "lowcoder-sdk"; import { trans } from "i18n/comps"; -import { examplesUrl, optionUrl } from "./chartConfigs/chartUrls"; +import { examplesUrl, mapExamplesUrl, mapOptionUrl, optionUrl } from "./chartConfigs/chartUrls"; export function chartPropertyView( children: ChartCompChildrenType, @@ -175,12 +175,12 @@ export function chartPropertyView( styleName: "higher", tooltip: ( ), diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts index a9cdb829d..e64f7694d 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts @@ -57,6 +57,8 @@ export const en = { echartsOptionLabel: "Option", echartsOptionTooltip: "ECharts option", echartsOptionExamples: "ECharts examples", + echartsMapOptionTooltip: "ECharts Map Option", + echartsMapOptionExamples: "ECharts Map Examples", selectDesc: "Triggered when the user selects part of the data in the chart", unselectDesc: "Triggered when the user unselects part of the data in the chart", selectedPointsDesc: "Selected points", diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/zh.ts b/client/packages/lowcoder-comps/src/i18n/comps/locales/zh.ts index cf27ca875..281d6e65e 100644 --- a/client/packages/lowcoder-comps/src/i18n/comps/locales/zh.ts +++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/zh.ts @@ -56,6 +56,8 @@ export const zh = { echartsOptionLabel: "选项", echartsOptionTooltip: "ECharts选项", echartsOptionExamples: "ECharts示例", + echartsMapOptionTooltip: "ECharts地图选项", + echartsMapOptionExamples: "ECharts地图示例", selectDesc: "当用户选择图表中的部分数据时触发", unselectDesc: "当用户取消选择图表中的部分数据时触发", selectedPointsDesc: "已选中的数据点",