mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-15 21:23:29 +08:00
7838
ui/package-lock.json
generated
7838
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
||||
"@c-x/ui": "^1.0.9",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@mui/icons-material": "^6.4.12",
|
||||
"@mui/lab": "6.0.0-beta.19",
|
||||
@@ -39,11 +40,12 @@
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite-plugin-dts": "^4.5.4"
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"zod": "^4.0.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@c-x/cx-swagger-api": "^0.0.10",
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
|
||||
28
ui/pnpm-lock.yaml
generated
28
ui/pnpm-lock.yaml
generated
@@ -17,6 +17,9 @@ importers:
|
||||
'@emotion/styled':
|
||||
specifier: ^11.14.0
|
||||
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
|
||||
'@hookform/resolvers':
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1(react-hook-form@7.62.0(react@19.1.1))
|
||||
'@monaco-editor/react':
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
@@ -95,12 +98,15 @@ importers:
|
||||
vite-plugin-dts:
|
||||
specifier: ^4.5.4
|
||||
version: 4.5.4(@types/node@24.2.1)(rollup@4.46.2)(typescript@5.8.3)(vite@6.3.5(@types/node@24.2.1)(jiti@2.5.1))
|
||||
zod:
|
||||
specifier: ^4.0.17
|
||||
version: 4.0.17
|
||||
devDependencies:
|
||||
'@c-x/cx-swagger-api':
|
||||
specifier: ^0.0.10
|
||||
version: 0.0.10(@types/node@24.2.1)(typescript@5.8.3)
|
||||
'@eslint/js':
|
||||
specifier: ^9.25.0
|
||||
specifier: ^9.33.0
|
||||
version: 9.33.0
|
||||
'@types/react':
|
||||
specifier: ^19.1.2
|
||||
@@ -534,6 +540,11 @@ packages:
|
||||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@hookform/resolvers@5.2.1':
|
||||
resolution: {integrity: sha512-u0+6X58gkjMcxur1wRWokA7XsiiBJ6aK17aPZxhkoYiK5J+HcTx0Vhu9ovXe6H+dVpO6cjrn2FkJTryXEMlryQ==}
|
||||
peerDependencies:
|
||||
react-hook-form: ^7.55.0
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@@ -921,6 +932,9 @@ packages:
|
||||
'@shikijs/vscode-textmate@10.0.2':
|
||||
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||
|
||||
'@standard-schema/utils@0.3.0':
|
||||
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
|
||||
|
||||
'@types/argparse@1.0.38':
|
||||
resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
|
||||
|
||||
@@ -2744,6 +2758,9 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zod@4.0.17:
|
||||
resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==}
|
||||
|
||||
zrender@5.6.1:
|
||||
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
||||
|
||||
@@ -3128,6 +3145,11 @@ snapshots:
|
||||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@hookform/resolvers@5.2.1(react-hook-form@7.62.0(react@19.1.1))':
|
||||
dependencies:
|
||||
'@standard-schema/utils': 0.3.0
|
||||
react-hook-form: 7.62.0(react@19.1.1)
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
@@ -3492,6 +3514,8 @@ snapshots:
|
||||
|
||||
'@shikijs/vscode-textmate@10.0.2': {}
|
||||
|
||||
'@standard-schema/utils@0.3.0': {}
|
||||
|
||||
'@types/argparse@1.0.38': {}
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
@@ -5715,6 +5739,8 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zod@4.0.17: {}
|
||||
|
||||
zrender@5.6.1:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
||||
166
ui/src/api/AiEmployee.ts
Normal file
166
ui/src/api/AiEmployee.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import request, { ContentType, RequestParams } from "./httpClient";
|
||||
import {
|
||||
DomainAIEmployee,
|
||||
DomainCreateAIEmployeeReq,
|
||||
DomainListAIEmployeeResp,
|
||||
DomainUUIDReq,
|
||||
DomainUpdateAIEmployeeReq,
|
||||
GetAiemployeeInfoParams,
|
||||
GetAiemployeeListParams,
|
||||
WebResp,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* @description 获取AI员工列表
|
||||
*
|
||||
* @tags AIEmployee
|
||||
* @name GetAiemployeeList
|
||||
* @summary 获取AI员工列表
|
||||
* @request GET:/api/v1/aiemployee
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainListAIEmployeeResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getAiemployeeList = (
|
||||
query: GetAiemployeeListParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainListAIEmployeeResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/aiemployee`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 更新AI员工
|
||||
*
|
||||
* @tags AIEmployee
|
||||
* @name PutAiemployeeUpdate
|
||||
* @summary 更新AI员工
|
||||
* @request PUT:/api/v1/aiemployee
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAIEmployee,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const putAiemployeeUpdate = (
|
||||
param: DomainUpdateAIEmployeeReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAIEmployee;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/aiemployee`,
|
||||
method: "PUT",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 创建AI员工
|
||||
*
|
||||
* @tags AIEmployee
|
||||
* @name PostAiemployeeCreate
|
||||
* @summary 创建AI员工
|
||||
* @request POST:/api/v1/aiemployee
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAIEmployee,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const postAiemployeeCreate = (
|
||||
param: DomainCreateAIEmployeeReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAIEmployee;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/aiemployee`,
|
||||
method: "POST",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 删除AI员工
|
||||
*
|
||||
* @tags AIEmployee
|
||||
* @name DeleteAiemployeeDelete
|
||||
* @summary 删除AI员工
|
||||
* @request DELETE:/api/v1/aiemployee
|
||||
* @response `200` `WebResp` OK
|
||||
*/
|
||||
|
||||
export const deleteAiemployeeDelete = (
|
||||
param: DomainUUIDReq,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<WebResp>({
|
||||
path: `/api/v1/aiemployee`,
|
||||
method: "DELETE",
|
||||
body: param,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取AI员工详情
|
||||
*
|
||||
* @tags AIEmployee
|
||||
* @name GetAiemployeeInfo
|
||||
* @summary 获取AI员工详情
|
||||
* @request GET:/api/v1/aiemployee/info
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainAIEmployee,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getAiemployeeInfo = (
|
||||
query: GetAiemployeeInfoParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainAIEmployee;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/aiemployee/info`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './Admin'
|
||||
export * from './AiEmployee'
|
||||
export * from './Billing'
|
||||
export * from './Cli'
|
||||
export * from './CodeSnippet'
|
||||
|
||||
@@ -156,22 +156,39 @@ export enum ConstsAIEmployeePosition {
|
||||
}
|
||||
|
||||
export interface DomainAIEmployee {
|
||||
/** 管理员 */
|
||||
admin?: DomainAdminUser;
|
||||
/** 完成的任务数 */
|
||||
completed_count?: number;
|
||||
/** 创建时间 */
|
||||
created_at?: number;
|
||||
id?: string;
|
||||
/** 是否在issue评论中@工程师 */
|
||||
issue_at_comment?: boolean;
|
||||
/** 是否处理新Issues */
|
||||
issue_open?: boolean;
|
||||
/** 最后活跃时间 */
|
||||
last_active_at?: number;
|
||||
/** 是否mr/pr在评论中@工程师 */
|
||||
mr_pr_at_comment?: boolean;
|
||||
/** 是否处理全部新增PR/MR */
|
||||
mr_pr_open?: boolean;
|
||||
/** 名称 */
|
||||
name?: string;
|
||||
/** 仓库平台 */
|
||||
platform?: ConstsRepoPlatform;
|
||||
/** 职位 */
|
||||
position?: ConstsAIEmployeePosition;
|
||||
/** 仓库 URL */
|
||||
repository_url?: string;
|
||||
/** 仓库用户名 */
|
||||
repository_user?: string;
|
||||
/** 仓库 token */
|
||||
token?: string;
|
||||
/** 仓库 webhook 密钥 */
|
||||
webhook_secret?: string;
|
||||
/** 仓库 webhook URL */
|
||||
webhook_url?: string;
|
||||
}
|
||||
|
||||
export interface DomainAcceptCompletionReq {
|
||||
@@ -396,10 +413,17 @@ export interface DomainCreateAIEmployeeReq {
|
||||
mr_pr_at_comment?: boolean;
|
||||
/** 是否处理全部新增PR/MR */
|
||||
mr_pr_open?: boolean;
|
||||
/** AI 员工名称 */
|
||||
name?: string;
|
||||
/** 仓库平台 */
|
||||
platform?: ConstsRepoPlatform;
|
||||
/** 职位 */
|
||||
position?: ConstsAIEmployeePosition;
|
||||
/** 仓库 URL */
|
||||
repo_url?: string;
|
||||
/** 仓库用户名 */
|
||||
repo_user?: string;
|
||||
/** 仓库 token */
|
||||
token?: string;
|
||||
}
|
||||
|
||||
@@ -1126,6 +1150,7 @@ export interface DomainUpdateAIEmployeeReq {
|
||||
platform?: ConstsRepoPlatform;
|
||||
position?: ConstsAIEmployeePosition;
|
||||
repo_url?: string;
|
||||
repo_user?: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ const ADMIN_BREADCRUMB_MAP: Record<string, { title: string; to: string }> = {
|
||||
chat: { title: '对话记录', to: '/chat' },
|
||||
completion: { title: '补全记录', to: '/completion' },
|
||||
codescan: { title: '代码安全', to: '/codescan' },
|
||||
model: { title: '模型管理', to: '/model' },
|
||||
employee: { title: 'AI 员工', to: '/employee' },
|
||||
'member-management': { title: '成员管理', to: '/member-management' },
|
||||
admin: { title: '管理员', to: '/admin' },
|
||||
};
|
||||
|
||||
@@ -41,11 +41,11 @@ const ADMIN_MENUS = [
|
||||
show: true,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
label: '模型管理',
|
||||
value: '/model',
|
||||
pathname: 'model',
|
||||
icon: 'icon-moxingguanli',
|
||||
{
|
||||
label: 'AI 员工',
|
||||
value: '/employee',
|
||||
pathname: 'employee',
|
||||
icon: 'icon-zhanghao',
|
||||
show: true,
|
||||
disabled: false,
|
||||
},
|
||||
@@ -137,7 +137,7 @@ const Sidebar = () => {
|
||||
}
|
||||
return isConfigModel
|
||||
? ADMIN_MENUS.map((item) => ({ ...item, disabled: false }))
|
||||
: ADMIN_MENUS.map((item) => ({ ...item, disabled: true }));
|
||||
: ADMIN_MENUS.map((item) => ({ ...item, disabled: item.pathname !== 'general-setting' }));
|
||||
}, [pathname, isConfigModel]);
|
||||
|
||||
return (
|
||||
@@ -208,7 +208,7 @@ const Sidebar = () => {
|
||||
>
|
||||
<Button
|
||||
variant={isActive ? 'contained' : 'text'}
|
||||
disabled={it.pathname === 'model' ? false : it.disabled}
|
||||
disabled={it.disabled}
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: 50,
|
||||
|
||||
@@ -88,8 +88,8 @@ const App = () => {
|
||||
|
||||
const handleModelConfig = (res: [DomainModel[], DomainModel[]]) => {
|
||||
if ((res[0] || [])?.length == 0 || (res[1] || [])?.length == 0) {
|
||||
if (location.pathname !== '/model') {
|
||||
window.location.href = '/model';
|
||||
if (location.pathname !== '/general-setting') {
|
||||
window.location.href = '/general-setting';
|
||||
}
|
||||
setIsConfigModel(false);
|
||||
return false;
|
||||
@@ -101,8 +101,8 @@ const App = () => {
|
||||
setIsConfigModel(true);
|
||||
return true;
|
||||
} else {
|
||||
if (location.pathname !== '/model') {
|
||||
window.location.href = '/model';
|
||||
if (location.pathname !== '/general-setting') {
|
||||
window.location.href = '/general-setting';
|
||||
}
|
||||
setIsConfigModel(false);
|
||||
return false;
|
||||
|
||||
302
ui/src/pages/employee/emloyeeModal.tsx
Normal file
302
ui/src/pages/employee/emloyeeModal.tsx
Normal file
@@ -0,0 +1,302 @@
|
||||
import {
|
||||
ConstsAIEmployeePosition,
|
||||
ConstsRepoPlatform,
|
||||
DomainAIEmployee,
|
||||
DomainUpdateAIEmployeeReq,
|
||||
postAiemployeeCreate,
|
||||
putAiemployeeUpdate,
|
||||
} from "@/api";
|
||||
import { Ellipsis, message, Modal } from "@c-x/ui";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import {
|
||||
Box,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Stack,
|
||||
TextField
|
||||
} from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import CopyToClipboard from "react-copy-to-clipboard";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
issue_at_comment: z.boolean().default(false),
|
||||
/** 是否处理新Issues */
|
||||
issue_open: z.boolean().default(false),
|
||||
/** 是否mr/pr在评论中@工程师 */
|
||||
mr_pr_at_comment: z.boolean().default(false),
|
||||
/** 是否处理全部新增PR/MR */
|
||||
mr_pr_open: z.boolean().default(false),
|
||||
name: z.string().min(1, "必填项").default(""),
|
||||
platform: z
|
||||
.enum(ConstsRepoPlatform)
|
||||
.default(ConstsRepoPlatform.RepoPlatformGitLab),
|
||||
position: z
|
||||
.enum(ConstsAIEmployeePosition)
|
||||
.default(ConstsAIEmployeePosition.AIEmployeePositionEngineer),
|
||||
repo_url: z.string().min(1, "必填项").default(""),
|
||||
token: z.string().min(1, "必填项").default(""),
|
||||
});
|
||||
|
||||
const EmloyeeModal = ({
|
||||
open,
|
||||
onClose,
|
||||
onChanged, // 添加一个回调函数,用于在创建成功后刷新列表
|
||||
record,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onChanged?: () => void; // 可选的回调函数
|
||||
record?: DomainUpdateAIEmployeeReq;
|
||||
}) => {
|
||||
const [webhookOpen, setWebhookOpen] = useState(false);
|
||||
const [webhookUrl, setWebhookUrl] = useState<
|
||||
Pick<DomainAIEmployee, "webhook_url" | "webhook_secret"> | undefined
|
||||
>();
|
||||
const {
|
||||
reset,
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: formSchema.parse({}),
|
||||
});
|
||||
const handleChange = handleSubmit(
|
||||
async (data) => {
|
||||
const res = await (record
|
||||
? putAiemployeeUpdate({ ...data, id: record.id })
|
||||
: postAiemployeeCreate(data));
|
||||
onChanged?.(); // 调用回调函数,刷新列表
|
||||
setWebhookUrl(res);
|
||||
setWebhookOpen(true);
|
||||
},
|
||||
(e) => {
|
||||
console.log(e);
|
||||
}
|
||||
);
|
||||
const checkitems = [
|
||||
{ key: "issue_open", label: "自动跟进所有的 Issue" },
|
||||
{ key: "mr_pr_open", label: "自动跟进所有的 Merge/Pull Request" },
|
||||
{ key: "issue_at_comment", label: "允许在 Issue 中被 @" },
|
||||
{ key: "mr_pr_at_comment", label: "允许在 Merge/Pull Request 中被 @" },
|
||||
] as const;
|
||||
useEffect(() => {
|
||||
if (open) reset(record || formSchema.parse({}));
|
||||
}, [record, reset, open]);
|
||||
const onCloseWebhook = () => {
|
||||
setWebhookOpen(false);
|
||||
setWebhookUrl(undefined);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={record ? "编辑 AI 员工" : "创建 AI 员工"}
|
||||
width={600}
|
||||
open={open}
|
||||
// onOk={() => setOpenWebhook(true)}
|
||||
onOk={handleChange}
|
||||
onCancel={onClose}
|
||||
okText={record ? "更新" : "创建"}
|
||||
cancelText="取消"
|
||||
>
|
||||
<Stack spacing={2} sx={{ fontSize: "13px" }}>
|
||||
<TextField
|
||||
label="AI 员工名称"
|
||||
fullWidth
|
||||
size="small"
|
||||
{...register("name")}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
component={FormControl}
|
||||
alignItems="center"
|
||||
spacing={3}
|
||||
>
|
||||
<FormLabel id="demo-row-radio-buttons-group-label">
|
||||
AI 员工角色
|
||||
</FormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name="position"
|
||||
render={({ field }) => (
|
||||
<RadioGroup
|
||||
row
|
||||
value={field.value}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
}}
|
||||
>
|
||||
{[
|
||||
ConstsAIEmployeePosition.AIEmployeePositionEngineer,
|
||||
ConstsAIEmployeePosition.AIEmployeePositionTester,
|
||||
ConstsAIEmployeePosition.AIEmployeePositionProductManager,
|
||||
].map((item) => (
|
||||
<FormControlLabel
|
||||
key={item}
|
||||
value={item}
|
||||
control={<Radio />}
|
||||
label={item}
|
||||
disabled={
|
||||
item !==
|
||||
ConstsAIEmployeePosition.AIEmployeePositionEngineer
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
<FormGroup>
|
||||
{checkitems.map((item) => (
|
||||
<Controller
|
||||
key={item.key}
|
||||
control={control}
|
||||
name={item.key}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
sx={{ mt: -2 }}
|
||||
control={<Checkbox {...field} checked={field.value} />}
|
||||
label={item.label}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</FormGroup>
|
||||
<TextField
|
||||
label="工作项目的 Git 仓库"
|
||||
fullWidth
|
||||
size="small"
|
||||
{...register("repo_url")}
|
||||
error={!!errors.repo_url}
|
||||
helperText={errors.repo_url?.message}
|
||||
/>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
component={FormControl}
|
||||
alignItems="center"
|
||||
spacing={3}
|
||||
>
|
||||
<FormLabel id="demo-row-radio-buttons-group-label">
|
||||
Git 托管平台
|
||||
</FormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name="platform"
|
||||
render={({ field }) => (
|
||||
<RadioGroup
|
||||
row
|
||||
value={field.value}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
}}
|
||||
>
|
||||
{[
|
||||
ConstsRepoPlatform.RepoPlatformGitHub,
|
||||
ConstsRepoPlatform.RepoPlatformGitLab,
|
||||
ConstsRepoPlatform.RepoPlatformGitee,
|
||||
ConstsRepoPlatform.RepoPlatformGitea,
|
||||
].map((item) => (
|
||||
<FormControlLabel
|
||||
key={item}
|
||||
value={item}
|
||||
control={<Radio />}
|
||||
label={item}
|
||||
disabled={item !== ConstsRepoPlatform.RepoPlatformGitLab}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
<TextField
|
||||
label="Git 仓库的访问令牌"
|
||||
fullWidth
|
||||
size="small"
|
||||
{...register("token")}
|
||||
error={!!errors.token}
|
||||
helperText={errors.token?.message}
|
||||
/>
|
||||
</Stack>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Webhook 配置信息"
|
||||
width={830}
|
||||
open={webhookOpen}
|
||||
onOk={onCloseWebhook}
|
||||
onCancel={onCloseWebhook}
|
||||
showCancel={false}
|
||||
okText="确定"
|
||||
>
|
||||
{webhookUrl?.webhook_secret && (
|
||||
<Stack
|
||||
spacing={2}
|
||||
sx={{
|
||||
mt: 2,
|
||||
fontSize: "14px",
|
||||
"& > .MuiStack-root > div:nth-child(2)": {
|
||||
fontWeight: 600,
|
||||
bgcolor: "background.paper",
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
borderRadius: "4px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" alignItems={"center"} spacing={2}>
|
||||
<Box sx={{ flexShrink: 0, minWidth: "130px" }}>Webhook URL: </Box>
|
||||
<Ellipsis>{webhookUrl?.webhook_url}</Ellipsis>
|
||||
<CopyToClipboard
|
||||
text={webhookUrl?.webhook_url || ""}
|
||||
onCopy={() => {
|
||||
message.success("复制成功");
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
color="primary"
|
||||
size="small"
|
||||
sx={{ alignSelf: "flex-end" }}
|
||||
>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</CopyToClipboard>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems={"center"} spacing={2}>
|
||||
<Box sx={{ flexShrink: 0, minWidth: "130px" }}>
|
||||
Webhook Secret:{" "}
|
||||
</Box>
|
||||
<Ellipsis>{webhookUrl?.webhook_secret}</Ellipsis>
|
||||
<CopyToClipboard
|
||||
text={webhookUrl?.webhook_secret || ""}
|
||||
onCopy={() => {
|
||||
message.success("复制成功");
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
color="primary"
|
||||
size="small"
|
||||
sx={{ alignSelf: "flex-end" }}
|
||||
>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</CopyToClipboard>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmloyeeModal;
|
||||
302
ui/src/pages/employee/index.tsx
Normal file
302
ui/src/pages/employee/index.tsx
Normal file
@@ -0,0 +1,302 @@
|
||||
import { Ellipsis, Icon, message, Modal, Table } from "@c-x/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import Card from "@/components/card";
|
||||
import { Box, Button, Stack } from "@mui/material";
|
||||
|
||||
import { deleteAiemployeeDelete, getAiemployeeList } from "@/api/AiEmployee";
|
||||
import {
|
||||
ConstsRepoPlatform,
|
||||
DomainAIEmployee,
|
||||
DomainUpdateAIEmployeeReq,
|
||||
} from "@/api/types";
|
||||
import { ColumnsType } from "@c-x/ui/dist/Table";
|
||||
import dayjs from "dayjs";
|
||||
import EmloyeeModal from "./emloyeeModal";
|
||||
|
||||
const gitPlatformIcons = {
|
||||
[ConstsRepoPlatform.RepoPlatformGitHub]: "icon-github",
|
||||
[ConstsRepoPlatform.RepoPlatformGitLab]: "icon-gitlab",
|
||||
[ConstsRepoPlatform.RepoPlatformGitee]: "icon-gitee",
|
||||
[ConstsRepoPlatform.RepoPlatformGitea]: "icon-gitea",
|
||||
} as const;
|
||||
const EmployeeTaskList = () => {
|
||||
const [page, setPage] = useState(1);
|
||||
const [size, setSize] = useState(20);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dataSource, setDataSource] = useState<DomainAIEmployee[]>([]);
|
||||
const [detail, setDetail] = useState<DomainUpdateAIEmployeeReq | undefined>();
|
||||
const [open, setOpen] = useState(false);
|
||||
const onClose = () => {
|
||||
setOpen(false);
|
||||
setDetail(undefined);
|
||||
};
|
||||
const onChanged = () => {
|
||||
onClose();
|
||||
fetchData({});
|
||||
};
|
||||
|
||||
const fetchData = async (params: { page?: number; size?: number }) => {
|
||||
setLoading(true);
|
||||
const res = await getAiemployeeList({
|
||||
page: params.page || page,
|
||||
size: params.size || size,
|
||||
});
|
||||
setLoading(false);
|
||||
setTotal(res.total_count || 0);
|
||||
setDataSource(res.items || []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPage(1);
|
||||
fetchData({
|
||||
page: 1,
|
||||
});
|
||||
}, []);
|
||||
const handleEdit = (record: DomainAIEmployee) => {
|
||||
setOpen(true);
|
||||
setDetail({
|
||||
...record,
|
||||
repo_url: record.repository_url,
|
||||
repo_user: record.repository_user,
|
||||
});
|
||||
};
|
||||
const handleDelete = (record: DomainAIEmployee) => {
|
||||
Modal.confirm({
|
||||
title: "提示",
|
||||
okText: "删除",
|
||||
okButtonProps: {
|
||||
color: "error",
|
||||
},
|
||||
content: (
|
||||
<>
|
||||
确定要删除 AI 员工{" "}
|
||||
<Box component="span" sx={{ fontWeight: 700, color: "text.primary" }}>
|
||||
{record!.name}
|
||||
</Box>{" "}
|
||||
吗?
|
||||
</>
|
||||
),
|
||||
onOk: () => {
|
||||
deleteAiemployeeDelete({ id: record.id! }).then(() => {
|
||||
message.success("删除成功");
|
||||
fetchData({});
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
const columns: ColumnsType<DomainAIEmployee> = [
|
||||
{
|
||||
dataIndex: "name",
|
||||
title: "AI 员工",
|
||||
width: 240,
|
||||
render: (name, record) => {
|
||||
return (
|
||||
<Stack direction="column">
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Icon
|
||||
type="icon-jiqiren"
|
||||
sx={{ fontSize: 20, color: "text.main" }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{record?.name}
|
||||
</Box>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
color: "text.tertiary",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: "0.8rem",
|
||||
mt: "4px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
lineHeight: "16px",
|
||||
}}
|
||||
>
|
||||
{record?.position}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "工作项目",
|
||||
dataIndex: "platform",
|
||||
width: 300,
|
||||
render: (platform, record) => {
|
||||
return (
|
||||
<Stack direction="column">
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Icon
|
||||
type={gitPlatformIcons[record.platform as ConstsRepoPlatform]}
|
||||
sx={{ fontSize: 16, color: "text.main" }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{record?.repository_url
|
||||
? record?.repository_url.split("/").pop()
|
||||
: record?.platform}
|
||||
</Box>
|
||||
</Stack>
|
||||
<Ellipsis
|
||||
sx={{
|
||||
color: "text.secondary",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: "0.8rem",
|
||||
}}
|
||||
>
|
||||
{record?.repository_url}
|
||||
</Ellipsis>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "创建者",
|
||||
dataIndex: "creater",
|
||||
render: (creater, record) => {
|
||||
return (
|
||||
<Stack direction="column">
|
||||
<Box
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{record?.admin?.username}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: "text.secondary",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: "0.8rem",
|
||||
}}
|
||||
>
|
||||
{dayjs(Number(record?.created_at) * 1000).fromNow()}创建
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "工作状态",
|
||||
dataIndex: "status",
|
||||
render: (status, record) => {
|
||||
return (
|
||||
<Stack direction="column">
|
||||
<Box
|
||||
sx={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
共完成 {record?.completed_count || 0} 个任务
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: "text.secondary",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: "0.8rem",
|
||||
}}
|
||||
>
|
||||
{dayjs(Number(record?.last_active_at) * 1000).fromNow()}活跃
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
dataIndex: "opt",
|
||||
render: (status, record) => {
|
||||
return (
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Button variant="text" onClick={() => handleEdit(record)}>
|
||||
修改
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
color="error"
|
||||
onClick={() => handleDelete(record)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Card sx={{ flex: 1, height: "100%" }}>
|
||||
<Stack height="100%">
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
sx={{ mb: 2, alignSelf: "flex-end" }}
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
创建 AI 员工
|
||||
</Button>
|
||||
<Table
|
||||
sx={{ mx: -2, flexGrow: 1, overflow: "auto" }}
|
||||
PaginationProps={{
|
||||
sx: {
|
||||
pt: 2,
|
||||
mx: 2,
|
||||
},
|
||||
}}
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
page,
|
||||
pageSize: size,
|
||||
total,
|
||||
onChange: (page: number, size: number) => {
|
||||
setPage(page);
|
||||
setSize(size);
|
||||
fetchData({
|
||||
page: page,
|
||||
size: size,
|
||||
});
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<EmloyeeModal
|
||||
open={open}
|
||||
record={detail}
|
||||
onClose={onClose}
|
||||
onChanged={onChanged}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeTaskList;
|
||||
@@ -1,26 +1,26 @@
|
||||
import Card from '@/components/card';
|
||||
import { Box, Button, Stack, TextField } from "@mui/material"
|
||||
import { message } from '@c-x/ui';
|
||||
import { useEffect, useState } from "react"
|
||||
import { getGetSetting, putUpdateSetting } from '@/api/Admin';
|
||||
import { DomainUpdateSettingReq } from '@/api/types';
|
||||
import Card from "@/components/card";
|
||||
import { Box, Button, Stack, TextField } from "@mui/material";
|
||||
import { message } from "@c-x/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getGetSetting, putUpdateSetting } from "@/api/Admin";
|
||||
import { DomainUpdateSettingReq } from "@/api/types";
|
||||
|
||||
const CardServiceSettings = () => {
|
||||
const [baseURL, setBaseURL] = useState('');
|
||||
const [initialBaseURL, setInitialBaseURL] = useState('');
|
||||
const [baseURL, setBaseURL] = useState("");
|
||||
const [initialBaseURL, setInitialBaseURL] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInitialBaseURL = async () => {
|
||||
try {
|
||||
const response = await getGetSetting();
|
||||
const initialValue = response.base_url || '';
|
||||
const initialValue = response.base_url || "";
|
||||
setBaseURL(initialValue);
|
||||
setInitialBaseURL(initialValue);
|
||||
} catch (err: any) {
|
||||
message.error('Failed to fetch initial base URL:', err);
|
||||
message.error("Failed to fetch initial base URL:", err);
|
||||
// 如果获取失败,可以设置一个默认值或者保持空字符串
|
||||
setBaseURL('');
|
||||
setInitialBaseURL('');
|
||||
setBaseURL("");
|
||||
setInitialBaseURL("");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,11 +35,11 @@ const CardServiceSettings = () => {
|
||||
try {
|
||||
const parsedURL = new URL(url);
|
||||
// Check if the protocol is either http or https
|
||||
if (parsedURL.protocol !== 'http:' && parsedURL.protocol !== 'https:') {
|
||||
if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
|
||||
return false;
|
||||
}
|
||||
// Check if the URL is a base URL (no path or only root path)
|
||||
if (parsedURL.pathname !== '/' && parsedURL.pathname !== '') {
|
||||
if (parsedURL.pathname !== "/" && parsedURL.pathname !== "") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -51,7 +51,7 @@ const CardServiceSettings = () => {
|
||||
const handleSave = async () => {
|
||||
// Check if the baseURL is valid before saving
|
||||
if (baseURL && !isValidURL(baseURL)) {
|
||||
message.error('请输入一个有效的 URL 地址');
|
||||
message.error("请输入一个有效的 URL 地址");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,63 +60,84 @@ const CardServiceSettings = () => {
|
||||
base_url: baseURL,
|
||||
};
|
||||
await putUpdateSetting(setting);
|
||||
message.success('保存成功');
|
||||
message.success("保存成功");
|
||||
setInitialBaseURL(baseURL);
|
||||
} catch (err: any) {
|
||||
message.error('保存失败:', err);
|
||||
message.error("保存失败:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const hasValueChanged = baseURL !== initialBaseURL;
|
||||
|
||||
return <Card sx={{p : 0, borderBottom: '1px solid #e0e0e0'}}>
|
||||
<Box sx={{
|
||||
fontWeight: 'bold',
|
||||
px: 2,
|
||||
py: 1.5,
|
||||
bgcolor: 'rgb(248, 249, 250)',
|
||||
borderTopLeftRadius: '10px',
|
||||
borderTopRightRadius: '10px',
|
||||
}}>MonkeyCode 服务配置</Box>
|
||||
<Stack direction='column'>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Stack direction='row' alignItems={'center'} justifyContent={'space-between'} sx={{
|
||||
m: 2,
|
||||
height: 32,
|
||||
fontWeight: 'bold',
|
||||
}}>
|
||||
<Box sx={{
|
||||
'&::before': {
|
||||
content: '""',
|
||||
display: 'inline-block',
|
||||
width: 4,
|
||||
height: 12,
|
||||
bgcolor: 'common.black',
|
||||
borderRadius: '2px',
|
||||
mr: 1,
|
||||
},
|
||||
}}>MonkeyCode 服务连接地址</Box>
|
||||
{hasValueChanged && <Button variant="contained" size="small" onClick={handleSave}>保存</Button>}
|
||||
</Stack>
|
||||
<Box sx={{ m: 2 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={baseURL}
|
||||
onChange={handleBaseURLChange}
|
||||
placeholder={baseURL ? '' : window.location.origin}
|
||||
/>
|
||||
<Box sx={{
|
||||
mt: 1,
|
||||
fontSize: '0.75rem',
|
||||
color: 'warning.main',
|
||||
fontWeight: 'normal'
|
||||
}}>
|
||||
用于解决 VSCode 插件无法连接 MonkeyCode 服务的问题
|
||||
return (
|
||||
<Card sx={{ p: 0, mb: 2, borderBottom: "1px solid #e0e0e0" }}>
|
||||
<Box
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
px: 2,
|
||||
py: 1.5,
|
||||
bgcolor: "rgb(248, 249, 250)",
|
||||
borderTopLeftRadius: "10px",
|
||||
borderTopRightRadius: "10px",
|
||||
}}
|
||||
>
|
||||
MonkeyCode 服务配置
|
||||
</Box>
|
||||
<Stack direction="column">
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems={"center"}
|
||||
justifyContent={"space-between"}
|
||||
sx={{
|
||||
m: 2,
|
||||
height: 32,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
"&::before": {
|
||||
content: '""',
|
||||
display: "inline-block",
|
||||
width: 4,
|
||||
height: 12,
|
||||
bgcolor: "common.black",
|
||||
borderRadius: "2px",
|
||||
mr: 1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
MonkeyCode 服务连接地址
|
||||
</Box>
|
||||
{hasValueChanged && (
|
||||
<Button variant="contained" size="small" onClick={handleSave}>
|
||||
保存
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
<Box sx={{ m: 2 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={baseURL}
|
||||
onChange={handleBaseURLChange}
|
||||
placeholder={baseURL ? "" : window.location.origin}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
mt: 1,
|
||||
fontSize: "0.75rem",
|
||||
color: "warning.main",
|
||||
fontWeight: "normal",
|
||||
}}
|
||||
>
|
||||
用于解决 VSCode 插件无法连接 MonkeyCode 服务的问题
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
}
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardServiceSettings;
|
||||
export default CardServiceSettings;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import CardServiceSettings from './components/cardServiceSettings';
|
||||
import { Grid2 as Grid } from '@mui/material';
|
||||
import CardAdminUser from './components/cardAdminUser';
|
||||
import CardServiceSettings from "./components/cardServiceSettings";
|
||||
import { Grid2 as Grid } from "@mui/material";
|
||||
import CardAdminUser from "./components/cardAdminUser";
|
||||
import Model from "@/pages/model";
|
||||
|
||||
const GeneralSetting = () => {
|
||||
return (
|
||||
<Grid container spacing={2} sx={{ height: '100%' }}>
|
||||
<Grid size={6}>
|
||||
<CardServiceSettings />
|
||||
</Grid>
|
||||
<Grid size={6}>
|
||||
<CardAdminUser />
|
||||
</Grid>
|
||||
<Grid container spacing={2} sx={{ height: "100%" }}>
|
||||
<Grid size={6}>
|
||||
<CardServiceSettings />
|
||||
<Model />
|
||||
</Grid>
|
||||
<Grid size={6}>
|
||||
<CardAdminUser />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import StyledLabel from '@/components/label';
|
||||
import { Icon, Modal, message } from '@c-x/ui';
|
||||
import { addCommasToNumber } from '@/utils';
|
||||
import NoData from '@/assets/images/nodata.png';
|
||||
import { ModelModal, Model, DEFAULT_MODEL_PROVIDERS} from '@yokowu/modelkit-ui';
|
||||
import { ModelModal, DEFAULT_MODEL_PROVIDERS} from '@yokowu/modelkit-ui';
|
||||
import { localModelToModelKitModel, modelService } from '@/pages/model/components/services/modelService';
|
||||
|
||||
const ModelItem = ({
|
||||
@@ -300,7 +300,7 @@ const ModelCard: React.FC<IModelCardProps> = ({
|
||||
{data?.length > 0 ? (
|
||||
<Grid container spacing={2} sx={{ mt: 2 }}>
|
||||
{data.map((item) => (
|
||||
<Grid size={{ xs: 12, sm: 12, md: 12, lg: 6, xl: 4 }} key={item.id}>
|
||||
<Grid size={{ lg: 12, xl: 6 }} key={item.id}>
|
||||
<ModelItem data={item} onEdit={onEdit} refresh={refreshModel} />
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
@@ -41,6 +41,7 @@ const UserLogin = LazyLoadable(lazy(() => import('@/pages/user/login')));
|
||||
const Expectation = LazyLoadable(lazy(() => import('@/pages/expectation')));
|
||||
const UserChat = LazyLoadable(lazy(() => import('@/pages/user/chat')));
|
||||
const AdminCodeScan = LazyLoadable(lazy(() => import('@/pages/codescan')));
|
||||
const AdminEmployee = LazyLoadable(lazy(() => import('@/pages/employee')));
|
||||
const UserCodeScan = LazyLoadable(lazy(() => import('@/pages/user/codescan')));
|
||||
const UserCompletion = LazyLoadable(
|
||||
lazy(() => import('@/pages/user/completion'))
|
||||
@@ -77,8 +78,8 @@ const routerConfig = [
|
||||
element: <Completion />,
|
||||
},
|
||||
{
|
||||
path: 'model',
|
||||
element: <Model />,
|
||||
path: 'employee',
|
||||
element: <AdminEmployee />,
|
||||
},
|
||||
{
|
||||
path: 'member-management',
|
||||
|
||||
@@ -178,6 +178,8 @@ const lightTheme = createTheme(
|
||||
MuiFormLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: 'unset',
|
||||
fontSize: '0.8rem',
|
||||
fontFamily: 'var(--font-gilory), var(--font-HarmonyOS)',
|
||||
},
|
||||
asterisk: {
|
||||
@@ -192,6 +194,20 @@ const lightTheme = createTheme(
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiRadio: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontSize: '0.8rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiFormControlLabel: {
|
||||
styleOverrides: {
|
||||
label: {
|
||||
fontSize: '0.8rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
|
||||
Reference in New Issue
Block a user