From 92fc748a575690bf82094dc9d5d8fc7c5944d377 Mon Sep 17 00:00:00 2001 From: Timothy Yin Date: Sat, 26 Apr 2025 19:29:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=99=E5=AD=A6=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E9=83=A8=E5=88=86=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/mcp.json | 12 + api/aifn.ts | 97 +++++++ components/MarkdownRenderer.vue | 49 ++++ components/ai/Conversation.vue | 129 +++++++++ components/ai/GeneratedContent.vue | 110 ++++++++ components/ai/index.ts | 21 ++ components/app/Container.vue | 2 +- components/fn/teach/CaseGen.vue | 111 ++++++++ components/fn/teach/KnowledgeDiagram.vue | 25 ++ components/fn/teach/LessonPlan.vue | 155 ++++------- components/fn/teach/StdDesign.vue | 56 ++++ components/nav/Tertiary.vue | 1 + components/ui/accordion/Accordion.vue | 19 ++ components/ui/accordion/AccordionContent.vue | 24 ++ components/ui/accordion/AccordionItem.vue | 24 ++ components/ui/accordion/AccordionTrigger.vue | 39 +++ components/ui/accordion/index.ts | 4 + components/ui/auto-form/AutoForm.vue | 105 ++++++++ components/ui/auto-form/AutoFormField.vue | 45 ++++ .../ui/auto-form/AutoFormFieldArray.vue | 110 ++++++++ .../ui/auto-form/AutoFormFieldBoolean.vue | 41 +++ components/ui/auto-form/AutoFormFieldDate.vue | 57 ++++ components/ui/auto-form/AutoFormFieldEnum.vue | 49 ++++ components/ui/auto-form/AutoFormFieldFile.vue | 74 ++++++ .../ui/auto-form/AutoFormFieldInput.vue | 36 +++ .../ui/auto-form/AutoFormFieldNumber.vue | 32 +++ .../ui/auto-form/AutoFormFieldObject.vue | 78 ++++++ components/ui/auto-form/AutoFormLabel.vue | 14 + components/ui/auto-form/constant.ts | 40 +++ components/ui/auto-form/dependencies.ts | 92 +++++++ components/ui/auto-form/index.ts | 15 ++ components/ui/auto-form/interface.ts | 95 +++++++ components/ui/auto-form/utils.ts | 188 +++++++++++++ components/ui/calendar/Calendar.vue | 60 +++++ components/ui/calendar/CalendarCell.vue | 24 ++ .../ui/calendar/CalendarCellTrigger.vue | 38 +++ components/ui/calendar/CalendarGrid.vue | 24 ++ components/ui/calendar/CalendarGridBody.vue | 11 + components/ui/calendar/CalendarGridHead.vue | 11 + components/ui/calendar/CalendarGridRow.vue | 21 ++ components/ui/calendar/CalendarHeadCell.vue | 21 ++ components/ui/calendar/CalendarHeader.vue | 21 ++ components/ui/calendar/CalendarHeading.vue | 31 +++ components/ui/calendar/CalendarNextButton.vue | 32 +++ components/ui/calendar/CalendarPrevButton.vue | 32 +++ components/ui/calendar/index.ts | 12 + components/ui/checkbox/Checkbox.vue | 33 +++ components/ui/checkbox/index.ts | 1 + components/ui/radio-group/RadioGroup.vue | 25 ++ components/ui/radio-group/RadioGroupItem.vue | 41 +++ components/ui/radio-group/index.ts | 2 + components/ui/switch/Switch.vue | 39 +++ components/ui/switch/index.ts | 1 + nuxt.config.ts | 3 +- package.json | 4 + pages/course/prep/teach.vue | 28 +- pnpm-lock.yaml | 248 +++++++++++++----- tailwind.config.js | 152 ++++++----- 58 files changed, 2614 insertions(+), 250 deletions(-) create mode 100644 .vscode/mcp.json create mode 100644 api/aifn.ts create mode 100644 components/MarkdownRenderer.vue create mode 100644 components/ai/Conversation.vue create mode 100644 components/ai/GeneratedContent.vue create mode 100644 components/ai/index.ts create mode 100644 components/fn/teach/CaseGen.vue create mode 100644 components/fn/teach/KnowledgeDiagram.vue create mode 100644 components/fn/teach/StdDesign.vue create mode 100644 components/ui/accordion/Accordion.vue create mode 100644 components/ui/accordion/AccordionContent.vue create mode 100644 components/ui/accordion/AccordionItem.vue create mode 100644 components/ui/accordion/AccordionTrigger.vue create mode 100644 components/ui/accordion/index.ts create mode 100644 components/ui/auto-form/AutoForm.vue create mode 100644 components/ui/auto-form/AutoFormField.vue create mode 100644 components/ui/auto-form/AutoFormFieldArray.vue create mode 100644 components/ui/auto-form/AutoFormFieldBoolean.vue create mode 100644 components/ui/auto-form/AutoFormFieldDate.vue create mode 100644 components/ui/auto-form/AutoFormFieldEnum.vue create mode 100644 components/ui/auto-form/AutoFormFieldFile.vue create mode 100644 components/ui/auto-form/AutoFormFieldInput.vue create mode 100644 components/ui/auto-form/AutoFormFieldNumber.vue create mode 100644 components/ui/auto-form/AutoFormFieldObject.vue create mode 100644 components/ui/auto-form/AutoFormLabel.vue create mode 100644 components/ui/auto-form/constant.ts create mode 100644 components/ui/auto-form/dependencies.ts create mode 100644 components/ui/auto-form/index.ts create mode 100644 components/ui/auto-form/interface.ts create mode 100644 components/ui/auto-form/utils.ts create mode 100644 components/ui/calendar/Calendar.vue create mode 100644 components/ui/calendar/CalendarCell.vue create mode 100644 components/ui/calendar/CalendarCellTrigger.vue create mode 100644 components/ui/calendar/CalendarGrid.vue create mode 100644 components/ui/calendar/CalendarGridBody.vue create mode 100644 components/ui/calendar/CalendarGridHead.vue create mode 100644 components/ui/calendar/CalendarGridRow.vue create mode 100644 components/ui/calendar/CalendarHeadCell.vue create mode 100644 components/ui/calendar/CalendarHeader.vue create mode 100644 components/ui/calendar/CalendarHeading.vue create mode 100644 components/ui/calendar/CalendarNextButton.vue create mode 100644 components/ui/calendar/CalendarPrevButton.vue create mode 100644 components/ui/calendar/index.ts create mode 100644 components/ui/checkbox/Checkbox.vue create mode 100644 components/ui/checkbox/index.ts create mode 100644 components/ui/radio-group/RadioGroup.vue create mode 100644 components/ui/radio-group/RadioGroupItem.vue create mode 100644 components/ui/radio-group/index.ts create mode 100644 components/ui/switch/Switch.vue create mode 100644 components/ui/switch/index.ts diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 0000000..5949e09 --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,12 @@ +{ + "servers": { + "xtms - API 文档": { + "command": "npx", + "args": [ + "-y", + "apifox-mcp-server@latest", + "--site-id=5442896" + ] + } + } +} \ No newline at end of file diff --git a/api/aifn.ts b/api/aifn.ts new file mode 100644 index 0000000..578fd5f --- /dev/null +++ b/api/aifn.ts @@ -0,0 +1,97 @@ +import type { IResponse } from '.' +import type { AIGeneratedContent } from '~/components/ai' + +export type AIGeneratedContentResponse = IResponse<{ + data: AIGeneratedContent & { + raw: string + } +}> + +export const AGCStream = async ( + endpoint: string, + params: ReqT, + events: { + onTextChunk?: (message: string) => void + onComplete?: () => void + }) => { + const { onTextChunk: onMessage, onComplete } = events + + const loginState = useLoginState() + + const runtimeConfig = useRuntimeConfig() + const baseURL = runtimeConfig.public.baseURL as string + + const response = await fetch(new URL(endpoint, baseURL), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'text/event-stream', + 'Authorization': `Bearer ${loginState.token}`, + }, + body: JSON.stringify(params), + }) + + if (!response) { + throw new Error('Network response was not ok') + } + + const reader = response.body?.getReader() + const decoder = new TextDecoder('utf-8') + let buffer = '' + + while (true) { + const { done, value } = await reader?.read() || {} + if (done) break + + buffer += decoder.decode(value, { stream: true }) + + const parts = buffer.split('\n\n') + buffer = parts.pop()! + + for (const part of parts) { + if (!part.trim().startsWith('data:')) continue + const payload = part.replace(/^data:\s*/, '').trim() + + try { + const obj = JSON.parse(payload) + if (obj?.event && obj.event === 'workflow_finished') { + onComplete?.() + return + } + if (obj?.event && obj.event === 'text_chunk') { + let text_chunk = obj.data?.text as string + if (text_chunk) { + if (text_chunk.startsWith('<') && text_chunk.endsWith('>')) { + const endTag = text_chunk.match(/<\/[^>]*>/) + if (endTag) { + text_chunk = text_chunk.replace(endTag[0], '\n' + endTag[0]) + } + } + onMessage?.(text_chunk) + } + } + } catch { + // ignore + } + } + } + onComplete?.() +} + +export const generateLessonPlan = async (params: { + query: string +}) => { + return await http(`/ai/lesson-plan-design/text-block`, { + method: 'POST', + body: params, + }) +} + +export const generateCase = async (params: { + query: string +}) => { + return await http(`/ai/case-design/text-block`, { + method: 'POST', + body: params, + }) +} diff --git a/components/MarkdownRenderer.vue b/components/MarkdownRenderer.vue new file mode 100644 index 0000000..ff4bc0c --- /dev/null +++ b/components/MarkdownRenderer.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/components/ai/Conversation.vue b/components/ai/Conversation.vue new file mode 100644 index 0000000..af25735 --- /dev/null +++ b/components/ai/Conversation.vue @@ -0,0 +1,129 @@ + + + +