diff --git a/ui/package.json b/ui/package.json
index 26a74a7..a46043f 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -14,6 +14,7 @@
"@c-x/ui": "^1.0.9",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
+ "@monaco-editor/react": "4.7.0-rc.0",
"@mui/icons-material": "^6.4.12",
"@mui/lab": "6.0.0-beta.19",
"@mui/material": "^6.4.12",
diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml
index b3ec09f..ad5893d 100644
--- a/ui/pnpm-lock.yaml
+++ b/ui/pnpm-lock.yaml
@@ -17,6 +17,9 @@ importers:
'@emotion/styled':
specifier: ^11.14.0
version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
+ '@monaco-editor/react':
+ specifier: 4.7.0-rc.0
+ version: 4.7.0-rc.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mui/icons-material':
specifier: ^6.4.12
version: 6.4.12(@mui/material@6.4.12(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
@@ -548,6 +551,16 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@monaco-editor/loader@1.5.0':
+ resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==}
+
+ '@monaco-editor/react@4.7.0-rc.0':
+ resolution: {integrity: sha512-YfjXkDK0bcwS0zo8PXptvQdCQfOPPtzGsAzmIv7PnoUGFdIohsR+NVDyjbajMddF+3cWUm/3q9NzP/DUke9a+w==}
+ peerDependencies:
+ monaco-editor: '>= 0.25.0 < 1'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
'@mui/base@5.0.0-beta.66':
resolution: {integrity: sha512-1SzcNbtIms0o/Dx+599B6QbvR5qUMBUjwc2Gs47h1HsF7RcEFXxqaq7zrWkIWbvGctIIPx0j330oGx/SkF+UmA==}
engines: {node: '>=14.0.0'}
@@ -1836,6 +1849,9 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
+ monaco-editor@0.52.2:
+ resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -2203,6 +2219,9 @@ packages:
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+ state-local@1.0.7:
+ resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
+
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -2843,6 +2862,17 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
+ '@monaco-editor/loader@1.5.0':
+ dependencies:
+ state-local: 1.0.7
+
+ '@monaco-editor/react@4.7.0-rc.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@monaco-editor/loader': 1.5.0
+ monaco-editor: 0.52.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
'@mui/base@5.0.0-beta.66(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@babel/runtime': 7.27.6
@@ -4363,6 +4393,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.2
+ monaco-editor@0.52.2: {}
+
ms@2.1.3: {}
nanoid@3.3.11: {}
@@ -4783,6 +4815,8 @@ snapshots:
space-separated-tokens@2.0.2: {}
+ state-local@1.0.7: {}
+
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
diff --git a/ui/src/api/types.ts b/ui/src/api/types.ts
index e85cc37..16e8cb1 100644
--- a/ui/src/api/types.ts
+++ b/ui/src/api/types.ts
@@ -131,6 +131,7 @@ export interface DomainCompletionInfo {
content?: string;
created_at?: number;
id?: string;
+ prompt?: string;
}
export interface DomainCompletionRecord {
@@ -330,11 +331,13 @@ export interface DomainProviderModel {
export interface DomainRegisterReq {
/** 邀请码 */
- code?: string;
+ code: string;
/** 邮箱 */
- email?: string;
+ email: string;
/** 密码 */
- password?: string;
+ password: string;
+ /** 用户名 */
+ username: string;
}
export interface DomainSetting {
@@ -585,6 +588,12 @@ export interface GetChatInfoParams {
}
export interface GetListChatRecordParams {
+ /** 作者 */
+ author?: string;
+ /** 是否接受筛选 */
+ is_accept?: boolean;
+ /** 语言 */
+ language?: string;
/** 下一页标识 */
next_token?: string;
/** 分页 */
@@ -599,6 +608,12 @@ export interface GetCompletionInfoParams {
}
export interface GetListCompletionRecordParams {
+ /** 作者 */
+ author?: string;
+ /** 是否接受筛选 */
+ is_accept?: boolean;
+ /** 语言 */
+ language?: string;
/** 下一页标识 */
next_token?: string;
/** 分页 */
diff --git a/ui/src/assets/fonts/iconfont.js b/ui/src/assets/fonts/iconfont.js
index acbe161..f682689 100644
--- a/ui/src/assets/fonts/iconfont.js
+++ b/ui/src/assets/fonts/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_4940939='',(c=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var t,o,i,h,e,n=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}t=function(){var a,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_4940939,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?n(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),t()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=t,h=c.document,e=!1,d(),h.onreadystatechange=function(){"complete"==h.readyState&&(h.onreadystatechange=null,m())})}function m(){e||(e=!0,i())}function d(){try{h.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}m()}})(window);
\ No newline at end of file
+window._iconfont_svg_string_4940939='',(c=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var t,o,i,h,e,n=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}t=function(){var a,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_4940939,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?n(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),t()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=t,h=c.document,e=!1,d(),h.onreadystatechange=function(){"complete"==h.readyState&&(h.onreadystatechange=null,m())})}function m(){e||(e=!0,i())}function d(){try{h.documentElement.doScroll("left")}catch(a){return void setTimeout(d,50)}m()}})(window);
\ No newline at end of file
diff --git a/ui/src/pages/completion/completionDetailModal.tsx b/ui/src/pages/completion/completionDetailModal.tsx
index 681e130..883d1be 100644
--- a/ui/src/pages/completion/completionDetailModal.tsx
+++ b/ui/src/pages/completion/completionDetailModal.tsx
@@ -1,16 +1,10 @@
-import Avatar from '@/components/avatar';
import Card from '@/components/card';
-import { getChatInfo } from '@/api/Billing';
-import MarkDown from '@/components/markDown';
-import { addCommasToNumber, processText } from '@/utils';
-import { Ellipsis, Icon, Modal } from '@c-x/ui';
-import { Box, Stack, Tooltip, useTheme } from '@mui/material';
-import dayjs from 'dayjs';
-import { useEffect, useState } from 'react';
-import { DomainCompletionRecord } from '@/api/types';
+import { getCompletionInfo } from '@/api/Billing';
+import { Modal } from '@c-x/ui';
+import MonacoEditor from '@monaco-editor/react';
-type ConversationItem = any;
-type ToolInfo = any;
+import { useEffect, useState, useRef } from 'react';
+import { DomainCompletionRecord } from '@/api/types';
const ChatDetailModal = ({
data,
@@ -21,51 +15,77 @@ const ChatDetailModal = ({
open: boolean;
onClose: () => void;
}) => {
- const theme = useTheme();
- const [ChatDetailModal, setChatDetailModal] =
- useState(null);
- const [content, setContent] = useState('');
- const [showToolInfo, setShowToolInfo] = useState<{ [key: string]: ToolInfo }>(
- {}
- );
+ const [editorValue, setEditorValue] = useState('');
+ const editorRef = useRef(null);
+ const [editorReady, setEditorReady] = useState(false);
+ const [highlightInfo, setHighlightInfo] = useState(null);
const getChatDetailModal = () => {
if (!data) return;
- getChatInfo({ id: data.id! }).then((res) => {
- setContent(
- data.program_language
- ? `\`\`\`${data.program_language}\n${res.content || ''}\n\`\`\``
- : res.content || ''
- );
+ getCompletionInfo({ id: data.id! }).then((res) => {
+ const rawPrompt = res.prompt || '';
+ const content = res.content || '';
+ // 找到三个特殊标记的位置
+ const prefixTag = '<|fim_prefix|>';
+ const suffixTag = '<|fim_suffix|>';
+ const middleTag = '<|fim_middle|>';
+ const prefixIdx = rawPrompt.indexOf(prefixTag);
+ const suffixIdx = rawPrompt.indexOf(suffixTag);
+ const middleIdx = rawPrompt.indexOf(middleTag);
+ // 去掉特殊标记
+ const prompt = rawPrompt
+ .replace(prefixTag, '')
+ .replace(suffixTag, '')
+ .replace(middleTag, '');
+ // 重新定位插入点(因为去掉了前面的 tag,位置会变)
+ // 计算插入点:suffixTag 在原始 prompt 的位置,去掉 prefixTag 后的 offset
+ let insertIdx = suffixIdx;
+ if (prefixIdx !== -1 && prefixIdx < suffixIdx) {
+ insertIdx -= prefixTag.length;
+ }
+ if (middleIdx !== -1 && middleIdx < suffixIdx) {
+ insertIdx -= middleTag.length;
+ }
+ // 插入 content
+ const newValue =
+ prompt.slice(0, insertIdx) + content + prompt.slice(insertIdx);
+ setEditorValue(newValue);
+ // 计算高亮范围(行列)
+ const before = newValue.slice(0, insertIdx);
+ const contentLines = content.split('\n');
+ const beforeLines = before.split('\n');
+ const startLine = beforeLines.length;
+ const startColumn = beforeLines[beforeLines.length - 1].length + 1;
+ const endLine = startLine + contentLines.length - 1;
+ const endColumn =
+ contentLines.length === 1
+ ? startColumn + content.length
+ : contentLines[contentLines.length - 1].length + 1;
+ setHighlightInfo({ startLine, startColumn, endLine, endColumn });
});
- // getConversationChatDetailModal({ id }).then((res) => {
- // const newAnswer = res.answer
- // const toolWrapsIds = newAnswer.match(//g)?.map(match => {
- // const idMatch = match.match(//);
- // return idMatch ? idMatch[1] : null;
- // }).filter(Boolean) || [];
- // const toolIds = newAnswer.match(//g)?.map(match => {
- // const idMatch = match.match(//);
- // return idMatch ? idMatch[1] : null;
- // }).filter(Boolean) || [];
- // const obj: { [key: string]: ToolInfo } = {}
- // toolWrapsIds.forEach(id => {
- // obj[id!] = {
- // done: true,
- // }
- // })
- // toolIds.forEach(id => {
- // obj[id!] = {
- // args: false,
- // result: false,
- // done: true,
- // }
- // })
- // setShowToolInfo(obj)
- // setChatDetailModal({ ...res, answer: processText(res.answer) })
- // })
};
+ useEffect(() => {
+ if (editorReady && highlightInfo && editorRef.current) {
+ editorRef.current.deltaDecorations(
+ [],
+ [
+ {
+ range: {
+ startLineNumber: highlightInfo.startLine,
+ startColumn: highlightInfo.startColumn,
+ endLineNumber: highlightInfo.endLine,
+ endColumn: highlightInfo.endColumn,
+ },
+ options: {
+ inlineClassName: 'completion-highlight',
+ },
+ },
+ ]
+ );
+ }
+ }, [editorReady, highlightInfo, editorValue]);
+
useEffect(() => {
if (open) getChatDetailModal();
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -79,164 +99,71 @@ const ChatDetailModal = ({
onCancel={onClose}
footer={null}
>
- {ChatDetailModal ? (
-
-
+
+
- {ChatDetailModal.created_at && (
-
-
- {dayjs(ChatDetailModal.created_at).format(
- 'YYYY-MM-DD HH:mm:ss'
- )}
-
- )}
- {ChatDetailModal.remote_ip && (
-
-
- {ChatDetailModal.remote_ip}
-
- )}
- {ChatDetailModal.model && (
-
-
- 使用模型
- {ChatDetailModal.model}
-
- )}
- {data?.input_tokens && data?.output_tokens && (
-
-
- 输入 Token 使用: {addCommasToNumber(data?.input_tokens)}
-
-
- 输出 Token 使用: {addCommasToNumber(data?.output_tokens)}
-
-
- }
- >
-
-
- Token 统计
-
- {addCommasToNumber(
- data?.input_tokens + data?.output_tokens
- )}
-
-
-
-
- )}
-
- {ChatDetailModal.references?.length > 0 && (
- <>
-
- 内容来源
-
-
- {ChatDetailModal.references.map((item: any, index: number) => (
-
-
- }
- />
-
-
- {item.title}
-
-
-
- ))}
-
- >
- )}
-
- 回答
-
-
- ) : (
-
- )}
-
-
+ onMount={(editor) => {
+ editorRef.current = editor;
+ setEditorReady(true);
+ // 隐藏光标
+ const editorDom = editor.getDomNode();
+ if (editorDom) {
+ const style = document.createElement('style');
+ style.innerHTML = `.monaco-editor .cursor { display: none !important; }`;
+ editorDom.appendChild(style);
+ }
+ }}
+ />
+
+
);
diff --git a/ui/src/pages/completion/constant.ts b/ui/src/pages/completion/constant.ts
new file mode 100644
index 0000000..3d62dfb
--- /dev/null
+++ b/ui/src/pages/completion/constant.ts
@@ -0,0 +1,67 @@
+export const LANG_OPTIONS = [
+ 'JavaScript',
+ 'JavaScriptReact',
+ 'TypeScript',
+ 'TypeScriptReact',
+ 'Python',
+ 'Java',
+ 'C',
+ 'C++',
+ 'C#',
+ 'Go',
+ 'PHP',
+ 'Ruby',
+ 'Swift',
+ 'Kotlin',
+ 'Rust',
+ 'Dart',
+ 'Objective-C',
+ 'Scala',
+ 'Perl',
+ 'R',
+ 'Shell Script',
+ 'PowerShell',
+ 'HTML',
+ 'CSS',
+ 'SCSS',
+ 'Less',
+ 'JSON',
+ 'YAML',
+ 'XML',
+ 'Markdown',
+ 'SQL',
+ 'GraphQL',
+ 'Dockerfile',
+ 'Makefile',
+ 'Lua',
+ 'Haskell',
+ 'Elixir',
+ 'Erlang',
+ 'F#',
+ 'Groovy',
+ 'Visual Basic',
+ 'Assembly',
+ 'Matlab',
+ 'Fortran',
+ 'COBOL',
+ 'Prolog',
+ 'Scheme',
+ 'Lisp',
+ 'Julia',
+ 'SASS',
+ 'TOML',
+ 'INI',
+ 'LaTeX',
+ 'CMake',
+ 'Batch',
+ 'CoffeeScript',
+ 'Crystal',
+ 'OCaml',
+ 'Nim',
+ 'ReScript',
+ 'Solidity',
+ 'Vue',
+ 'Svelte',
+ 'JSX',
+ 'TSX',
+];
diff --git a/ui/src/pages/completion/index.tsx b/ui/src/pages/completion/index.tsx
index 71cec74..334e164 100644
--- a/ui/src/pages/completion/index.tsx
+++ b/ui/src/pages/completion/index.tsx
@@ -1,15 +1,54 @@
-import React, { useState, useEffect } from 'react';
+import React, {
+ useState,
+ useEffect,
+ useMemo,
+ useCallback,
+ useRef,
+} from 'react';
import { DomainCompletionRecord } from '@/api/types';
import { getListCompletionRecord } from '@/api/Billing';
import { useRequest } from 'ahooks';
import { Table } from '@c-x/ui';
import Card from '@/components/card';
-import { Box, Button, ButtonBase, Chip, Stack, alpha } from '@mui/material';
+import {
+ Box,
+ Button,
+ ButtonBase,
+ Chip,
+ Stack,
+ alpha,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Autocomplete,
+ TextField,
+ InputAdornment,
+} from '@mui/material';
+import { getListUser } from '@/api/User';
import dayjs from 'dayjs';
import { ColumnsType } from '@c-x/ui/dist/Table';
import { addCommasToNumber } from '@/utils';
import CompletionDetailModal from './completionDetailModal';
import StyledLabel from '@/components/label';
+import { LANG_OPTIONS } from './constant';
+import { ArrowDropDown as ArrowDropDownIcon } from '@mui/icons-material';
+
+// 防抖 hook
+function useDebounce(fn: (...args: any[]) => void, delay: number) {
+ const timer = useRef(null);
+ const fnRef = useRef(fn);
+ fnRef.current = fn;
+ return useCallback(
+ (...args: any[]) => {
+ if (timer.current) clearTimeout(timer.current);
+ timer.current = setTimeout(() => {
+ fnRef.current(...args);
+ }, delay);
+ },
+ [delay]
+ );
+}
const Completion = () => {
const [page, setPage] = useState(1);
@@ -20,26 +59,60 @@ const Completion = () => {
const [completionDetailModal, setCompletionDetailModal] = useState<
DomainCompletionRecord | undefined
>();
- const fetchData = async () => {
+
+ // 新增筛选项 state
+ const [filterUser, setFilterUser] = useState('');
+ const [filterLang, setFilterLang] = useState('');
+ const [filterAccept, setFilterAccept] = useState<
+ 'accepted' | 'unaccepted' | ''
+ >('');
+
+ const { data: userOptions = { users: [] } } = useRequest(() =>
+ getListUser({
+ page: 1,
+ size: 99999,
+ })
+ );
+
+ useEffect(() => {
+ setPage(1); // 筛选变化时重置页码
+ fetchData({
+ page: 1,
+ language: filterLang,
+ author: filterUser,
+ is_accept: filterAccept,
+ });
+ }, [filterUser, filterLang, filterAccept]);
+
+ const fetchData = async (params: {
+ page?: number;
+ size?: number;
+ language?: string;
+ author?: string;
+ is_accept?: 'accepted' | 'unaccepted' | '';
+ }) => {
setLoading(true);
const res = await getListCompletionRecord({
- page: page,
- size: size,
+ page: params.page || page,
+ size: params.size || size,
+ language: params.language || filterLang,
+ author: params.author || filterUser,
+ is_accept:
+ params.is_accept === 'accepted'
+ ? true
+ : params.is_accept === 'unaccepted'
+ ? false
+ : undefined,
});
setLoading(false);
setTotal(res?.total_count || 0);
setDataSource(res.records || []);
};
- useEffect(() => {
- fetchData();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [page, size]);
const columns: ColumnsType = [
{
dataIndex: 'user',
title: '成员',
-
render(value: DomainCompletionRecord['user']) {
return value?.username;
},
@@ -60,7 +133,6 @@ const Completion = () => {
);
},
},
-
{
dataIndex: 'is_accept',
title: '是否采纳',
@@ -72,7 +144,6 @@ const Completion = () => {
);
},
},
-
{
dataIndex: 'program_language',
title: '编程语言',
@@ -88,7 +159,6 @@ const Completion = () => {
{
dataIndex: 'output_tokens',
title: '输出 Token',
-
render(value: number) {
return addCommasToNumber(value);
},
@@ -102,11 +172,72 @@ const Completion = () => {
},
},
];
+
+ const debounceSetFilterLang = useDebounce(
+ (val: string) => setFilterLang(val),
+ 500
+ );
+
return (
+ {/* 筛选项 */}
+
+ {/* 成员筛选 Autocomplete */}
+ option.username || ''}
+ value={
+ userOptions.users?.find((item) => item.username === filterUser) ||
+ null
+ }
+ onChange={(_, newValue) =>
+ setFilterUser(newValue ? newValue.username! : '')
+ }
+ isOptionEqualToValue={(option, value) =>
+ option.username === value.username
+ }
+ renderInput={(params) => }
+ clearOnEscape
+ />
+ {/* 语言筛选 Autocomplete */}
+ option || ''}
+ value={filterLang || ''}
+ freeSolo
+ onChange={(_, newValue) => {
+ setFilterLang(newValue ? String(newValue) : '');
+ }}
+ onInputChange={(_, newInputValue) =>
+ debounceSetFilterLang(newInputValue)
+ }
+ popupIcon={}
+ renderInput={(params) => }
+ clearOnEscape
+ />
+ {/* 是否采纳筛选 Select 保持不变 */}
+
+ 是否采纳
+
+
+
{
onChange: (page, size) => {
setPage(page);
setSize(size);
+ fetchData({
+ page,
+ size,
+ });
},
}}
/>
diff --git a/ui/src/pages/invite/index.tsx b/ui/src/pages/invite/index.tsx
index 777ba61..758b31c 100644
--- a/ui/src/pages/invite/index.tsx
+++ b/ui/src/pages/invite/index.tsx
@@ -50,7 +50,7 @@ const StyledPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(4),
background: 'rgba(255, 255, 255, 0.85)',
backdropFilter: 'blur(10px)',
- width: 600,
+ width: 500,
borderRadius: theme.spacing(2),
boxShadow:
'0px 0px 4px 0px rgba(54,59,76,0.1), 0px 20px 40px 0px rgba(54,59,76,0.1)',
@@ -67,6 +67,27 @@ const StepCard = styled(Box)(({ theme }) => ({
borderRadius: theme.spacing(2),
}));
+const IconWrapper = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ color: theme.palette.text.primary,
+ marginRight: theme.spacing(2),
+ fontSize: 16,
+}));
+
+const StyledTextField = styled(TextField)(({ theme }) => ({
+ '.MuiInputBase-root': {
+ backgroundColor: '#fff',
+ paddingLeft: '20px',
+ },
+ '.MuiInputBase-input': {
+ paddingTop: '16px',
+ paddingBottom: '16px',
+ fontSize: 14,
+ },
+}));
+
const Invite = () => {
const { id, step } = useParams();
const [showPassword, setShowPassword] = useState(false);
@@ -79,6 +100,7 @@ const Invite = () => {
defaultValues: {
email: '',
password: '',
+ username: '',
},
});
@@ -92,7 +114,7 @@ const Invite = () => {
};
const onRegister = handleSubmit((data) => {
- register({ ...data, code: id }).then(() => {
+ register({ ...data, code: id! }).then(() => {
onNext();
});
});
@@ -127,6 +149,35 @@ const Invite = () => {
return !loginSetting.enable_dingtalk_oauth ? (
+
+ (
+
+
+
+ ),
+ },
+ }}
+ />
+ )}
+ />
+
{
},
}}
render={({ field }) => (
- {
slotProps={{
input: {
startAdornment: (
-
+
+
+
),
},
}}
@@ -178,7 +224,7 @@ const Invite = () => {
},
}}
render={({ field }) => (
- {
slotProps={{
input: {
startAdornment: (
-
+
+
+
),
endAdornment: (
@@ -349,7 +390,7 @@ const Invite = () => {