From 62d14b41248255794feed55f03cc4263f0e2c640 Mon Sep 17 00:00:00 2001 From: liulei Date: Wed, 12 Jul 2023 02:01:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0openai=20apikey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/.prettierrc.json | 1 + client/package.json | 2 +- client/packages/lowcoder/src/api/openAiApi.ts | 20 ++- .../src/base/codeEditor/codeEditor.tsx | 134 ++++++++++++------ 4 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 client/.prettierrc.json diff --git a/client/.prettierrc.json b/client/.prettierrc.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/client/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/client/package.json b/client/package.json index ead2a6e5..ed06895d 100644 --- a/client/package.json +++ b/client/package.json @@ -10,7 +10,7 @@ "node": "^14.18.0 || >=16.0.0" }, "scripts": { - "start": " API_PROXY_TARGET=http://localhost:3000 yarn workspace lowcoder start", + "start": " API_PROXY_TARGET=http://192.168.31.46:3000 yarn workspace lowcoder start", "start:ee": "REACT_APP_EDITION=enterprise yarn workspace lowcoder start", "start:ee-global": "REACT_APP_EDITION=enterprise-global yarn workspace lowcoder start", "build": "yarn node ./scripts/build.js", diff --git a/client/packages/lowcoder/src/api/openAiApi.ts b/client/packages/lowcoder/src/api/openAiApi.ts index 053e6592..7e6e7f9c 100644 --- a/client/packages/lowcoder/src/api/openAiApi.ts +++ b/client/packages/lowcoder/src/api/openAiApi.ts @@ -1,11 +1,17 @@ -import axios, {AxiosResponse} from "axios"; +import axios, { AxiosResponse } from "axios"; const instance = axios.create({ - baseURL:'http://localhost:3008', - timeout:60 * 1000 -}) + baseURL: "http://localhost:3008", + timeout: 60 * 1000, +}); export class OpenAiApi { - static generateSQL({query}:{query:string}):Promise>{ - return instance.post('/low-code/generate-sql',{query}) + static generateSQL({ + query, + openAIApiKey, + }: { + query: string; + openAIApiKey: string; + }): Promise> { + return instance.post("/low-code/generate-sql", { query, openAIApiKey }); } -} \ No newline at end of file +} diff --git a/client/packages/lowcoder/src/base/codeEditor/codeEditor.tsx b/client/packages/lowcoder/src/base/codeEditor/codeEditor.tsx index 5e2444b9..e95c98d3 100644 --- a/client/packages/lowcoder/src/base/codeEditor/codeEditor.tsx +++ b/client/packages/lowcoder/src/base/codeEditor/codeEditor.tsx @@ -14,8 +14,8 @@ import { CodeEditorPanel } from "../../pages/editor/codeEditorPanel"; import { CodeEditorProps, StyleName } from "./codeEditorTypes"; import { useClickCompNameEffect } from "./clickCompName"; import { Layers } from "../../constants/Layers"; -import {Button, Input, Space} from "antd"; -import {OpenAiApi} from "../../api/openAiApi"; +import { Button, Input, Space } from "antd"; +import { OpenAiApi } from "../../api/openAiApi"; type StyleConfig = { minHeight: string; @@ -65,7 +65,9 @@ export const CodeEditorTooltipContainer = styled.div` .cm-tooltip .cm-tooltip-lint { background-color: #2c2c2c; border-radius: 4px; - box-shadow: 0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%), + box-shadow: + 0 3px 6px -4px rgb(0 0 0 / 12%), + 0 6px 16px 0 rgb(0 0 0 / 8%), 0 9px 28px 8px rgb(0 0 0 / 5%); color: #ffffff; padding: 3px 0px; @@ -202,27 +204,33 @@ export const CodeEditorTooltipContainer = styled.div` `; function getStyle(styleName?: StyleName) { - return styleName ? styles[styleName] : { minHeight: "auto", maxHeight: "180px" }; + return styleName + ? styles[styleName] + : { minHeight: "auto", maxHeight: "180px" }; } function useCodeMirror( props: CodeEditorProps, - container: MutableRefObject + container: MutableRefObject, ) { const { value, onChange } = props; const viewRef = useRef(); // will not trigger view.setState when typing inputs, to avoid focus chaos const isTypingRef = useRef(0); - const showLineNum = props.showLineNum ?? getStyle(props.styleName).showLineNum; + const showLineNum = + props.showLineNum ?? getStyle(props.styleName).showLineNum; const handleChange = useCallback( (state: EditorState) => { window.clearTimeout(isTypingRef.current); - isTypingRef.current = window.setTimeout(() => (isTypingRef.current = 0), 100); + isTypingRef.current = window.setTimeout( + () => (isTypingRef.current = 0), + 100, + ); onChange?.(state); }, - [onChange] + [onChange], ); const { extensions, reconfigure, isFocus } = useExtensions({ @@ -238,7 +246,10 @@ function useCodeMirror( * 1. switch to the same type of comp * 2. change the current comp's value by dispatchChangeValueAction */ - if (!view || (!isTypingRef.current && value !== view.state.doc.toString())) { + if ( + !view || + (!isTypingRef.current && value !== view.state.doc.toString()) + ) { const state = EditorState.create({ doc: value, extensions }); if (view) { view.setState(state); @@ -248,7 +259,12 @@ function useCodeMirror( } }, [container, value, extensions]); - useClickCompNameEffect(viewRef.current, value, props.codeType, props.exposingData); + useClickCompNameEffect( + viewRef.current, + value, + props.codeType, + props.exposingData, + ); useEffect(() => { return () => { @@ -335,7 +351,9 @@ const CodeEditorWrapper = styled.div` `; function canShowCard(props: CodeEditorProps) { - return !props.disableCard && (props.codeType !== "Function" || props.hasError); + return ( + !props.disableCard && (props.codeType !== "Function" || props.hasError) + ); } function CodeEditorCommon( @@ -344,44 +362,70 @@ function CodeEditorCommon( children: ReactNode; disabled?: boolean; cardStyle?: React.CSSProperties; - } + }, ) { - const { editor, children, disabled, cardStyle, onClick, ...editorProps } = props; + const { editor, children, disabled, cardStyle, onClick, ...editorProps } = + props; const { view, isFocus } = useCodeMirror(editorProps, editor); - const [prompt,setPrompt] = useState('') - const [copilotIng,setCopilotIng] = useState(false) - const generateAISQL = async (query:string) => { - if(!query){ - return + const [prompt, setPrompt] = useState(""); + const [openAIApiKey, setOpenAIApiKey] = useState( + localStorage.getItem("OPEN_AI_API_KEY") ?? "", + ); + useEffect(() => { + localStorage.setItem("OPEN_AI_API_KEY", openAIApiKey); + }, [openAIApiKey]); + const [copilotIng, setCopilotIng] = useState(false); + const generateAISQL = async (query: string) => { + if (!query || !openAIApiKey) { + return; } try { - setCopilotIng(true) - const res = await OpenAiApi.generateSQL({query}) - props.onChange?.( EditorState.create({doc:res.data.text})) - }finally { - setCopilotIng(false) + setCopilotIng(true); + const res = await OpenAiApi.generateSQL({ query, openAIApiKey }); + props.onChange?.(EditorState.create({ doc: res.data.text })); + } finally { + setCopilotIng(false); } - } + }; return ( - view && onClick(e, view) : undefined}> + view && onClick(e, view) : undefined} + > {!disabled && view && props.widgetPopup?.(view)} {children} - { - props.language === 'sql' &&
- setPrompt(e.target.value)}/> - + {props.language === "sql" && ( +
+
+ setPrompt(e.target.value)} + /> +
- } +
+ setOpenAIApiKey(e.target.value)} + /> +
+
+ )} (); return ( - + } @@ -429,7 +477,9 @@ export function CodeEditor(props: CodeEditorProps) { {expandable && ( } + editor={ + + } onVisibleChange={(visible) => setDisabled(visible)} /> )}