zhangkai 1 年間 前
コミット
e12f955105
100 ファイル変更1960 行追加403 行削除
  1. BIN
      .DS_Store
  2. BIN
      build/assets/icon1-22423762.png
  3. BIN
      build/assets/icon1-active-e898a63f.png
  4. BIN
      build/assets/icon2-0a5dbb41.png
  5. BIN
      build/assets/icon2-active-eae32509.png
  6. BIN
      build/assets/icon3-active-56cabe9a.png
  7. BIN
      build/assets/icon4-active-78d00c9a.png
  8. BIN
      build/assets/icon5-187cba94.png
  9. BIN
      build/assets/icon5-active-52a6d7fe.png
  10. 1 25
      build/index.html
  11. 116 6
      build/locales/en/bs.json
  12. 120 9
      build/locales/zh/bs.json
  13. 519 1
      package-lock.json
  14. 3 0
      package.json
  15. 116 6
      public/locales/en/bs.json
  16. 120 9
      public/locales/zh/bs.json
  17. BIN
      src/.DS_Store
  18. 62 68
      src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
  19. BIN
      src/assets/.DS_Store
  20. BIN
      src/assets/Login/.DS_Store
  21. BIN
      src/assets/Login/reset.png
  22. BIN
      src/assets/nav/.DS_Store
  23. BIN
      src/assets/nav/admin.png
  24. BIN
      src/assets/nav/admin1.png
  25. BIN
      src/assets/nav/icon1-active.png
  26. BIN
      src/assets/nav/icon1.png
  27. BIN
      src/assets/nav/icon2-active.png
  28. BIN
      src/assets/nav/icon2.png
  29. BIN
      src/assets/nav/icon3-active.png
  30. BIN
      src/assets/nav/icon3.png
  31. BIN
      src/assets/nav/icon4-active.png
  32. BIN
      src/assets/nav/icon4.png
  33. BIN
      src/assets/nav/icon5-active.png
  34. BIN
      src/assets/nav/icon5.png
  35. BIN
      src/assets/nav/icon6-active.png
  36. BIN
      src/assets/nav/icon6.png
  37. BIN
      src/assets/nav/icon7-active.png
  38. BIN
      src/assets/nav/icon7.png
  39. BIN
      src/assets/nav/mima.png
  40. BIN
      src/assets/nav/tuichu.png
  41. BIN
      src/components/.DS_Store
  42. 7 4
      src/components/VariablesComponent/index.tsx
  43. BIN
      src/components/bs-comp/.DS_Store
  44. 83 53
      src/components/bs-comp/chatComponent/ChatInput.tsx
  45. 3 1
      src/components/bs-comp/chatComponent/FileBs.tsx
  46. 1 2
      src/components/bs-comp/chatComponent/MessageBs.tsx
  47. 1 2
      src/components/bs-comp/chatComponent/MessagePanne.tsx
  48. 15 2
      src/components/bs-comp/chatComponent/index.tsx
  49. 48 13
      src/components/bs-comp/chatComponent/messageStore.ts
  50. 24 0
      src/components/bs-comp/loadMore/index.tsx
  51. 48 0
      src/components/bs-comp/selectComponent/Users.tsx
  52. 51 0
      src/components/bs-comp/selectComponent/knowledge.tsx
  53. 38 49
      src/components/bs-comp/sheets/SkillChatSheet.tsx
  54. BIN
      src/components/bs-icons/.DS_Store
  55. 3 3
      src/components/bs-icons/del/Del.svg
  56. 1 1
      src/components/bs-icons/del/index.tsx
  57. 7 0
      src/components/bs-icons/down/DropDown.svg
  58. 9 0
      src/components/bs-icons/down/index.tsx
  59. 1 1
      src/components/bs-icons/en/En.svg
  60. 6 0
      src/components/bs-icons/filter/Filter.svg
  61. 9 0
      src/components/bs-icons/filter/index.tsx
  62. 40 0
      src/components/bs-icons/index.ts
  63. 0 0
      src/components/bs-icons/menu/application/Application.svg
  64. 0 0
      src/components/bs-icons/menu/application/index.tsx
  65. 6 0
      src/components/bs-icons/menu/evaluation/Evaluation.svg
  66. 9 0
      src/components/bs-icons/menu/evaluation/index.tsx
  67. 6 0
      src/components/bs-icons/menu/log/Log.svg
  68. 9 0
      src/components/bs-icons/menu/log/index.tsx
  69. 0 0
      src/components/bs-icons/menu/model/Model.svg
  70. 0 0
      src/components/bs-icons/menu/model/index.tsx
  71. 0 0
      src/components/bs-icons/menu/system/System.svg
  72. 0 0
      src/components/bs-icons/menu/system/index.tsx
  73. 0 0
      src/components/bs-icons/menu/technology/Technology.svg
  74. 0 0
      src/components/bs-icons/menu/technology/index.tsx
  75. 1 1
      src/components/bs-icons/office/index.tsx
  76. 12 9
      src/components/bs-icons/plusBox/PlusBox.svg
  77. 1 1
      src/components/bs-icons/questionMark/index.tsx
  78. 0 8
      src/components/bs-icons/quit/Quit-dark.svg
  79. 10 7
      src/components/bs-icons/quit/Quit.svg
  80. 2 10
      src/components/bs-icons/quit/index.tsx
  81. 2 2
      src/components/bs-icons/search/Search.svg
  82. 3 3
      src/components/bs-icons/thumbs/copy.svg
  83. 0 6
      src/components/bs-icons/thumbs/copyDark.svg
  84. 2 8
      src/components/bs-icons/thumbs/index.tsx
  85. 4 4
      src/components/bs-icons/thumbs/like.svg
  86. 0 12
      src/components/bs-icons/thumbs/likeDark.svg
  87. 3 3
      src/components/bs-icons/thumbs/unLike.svg
  88. 0 14
      src/components/bs-icons/thumbs/unLikeDark.svg
  89. 1 1
      src/components/bs-icons/upload/index.tsx
  90. BIN
      src/components/bs-ui/.DS_Store
  91. 58 0
      src/components/bs-ui/calendar/datePicker.tsx
  92. 72 0
      src/components/bs-ui/calendar/index.tsx
  93. 1 1
      src/components/bs-ui/dialog/index.tsx
  94. 46 0
      src/components/bs-ui/input/avator.tsx
  95. 26 4
      src/components/bs-ui/input/index.tsx
  96. 1 1
      src/components/bs-ui/popover/index.tsx
  97. 57 0
      src/components/bs-ui/select/filter.tsx
  98. 23 0
      src/components/bs-ui/select/hover.tsx
  99. 3 3
      src/components/bs-ui/select/index.tsx
  100. 150 50
      src/components/bs-ui/select/multi.tsx

BIN
.DS_Store


BIN
build/assets/icon1-22423762.png


BIN
build/assets/icon1-active-e898a63f.png


BIN
build/assets/icon2-0a5dbb41.png


BIN
build/assets/icon2-active-eae32509.png


BIN
build/assets/icon3-active-56cabe9a.png


BIN
build/assets/icon4-active-78d00c9a.png


BIN
build/assets/icon5-187cba94.png


BIN
build/assets/icon5-active-52a6d7fe.png


+ 1 - 25
build/index.html

@@ -1,25 +1 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="icon" href="/favicon.ico" />
-    <script src="/node_modules/ace-builds/src-min-noconflict/ace.js" type="text/javascript"></script>
-    <title>NPCs</title>
-  <script type="module" crossorigin src="/assets/index-583c10b3.js"></script>
-  <link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js">
-  <link rel="modulepreload" crossorigin href="/assets/reactflow-c250d835.js">
-  <link rel="modulepreload" crossorigin href="/assets/reactdrop-be699031.js">
-  <link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js">
-  <link rel="stylesheet" href="/assets/index-1055532c.css">
-</head>
-
-<body id='body' style="width: 100%; height:100%">
-    <noscript>You need to enable JavaScript to run this app.</noscript>
-    <div style="width: 100vw; height:100vh" id='root'></div>
-    
-</body>
-
-</html>
+<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"/><script src="/node_modules/ace-builds/src-min-noconflict/ace.js"></script><title>NPCs</title><script type="module" crossorigin src="/assets/index-b35a88c5.js"></script><link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js"><link rel="modulepreload" crossorigin href="/assets/reactflow-c250d835.js"><link rel="modulepreload" crossorigin href="/assets/reactdrop-be699031.js"><link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js"><link rel="stylesheet" href="/assets/index-d15ff243.css"></head><body id="body" style="width:100%;height:100%"><noscript>You need to enable JavaScript to run this app.</noscript><div style="width:100vw;height:100vh" id="root"></div></body></html>

+ 116 - 6
build/locales/en/bs.json

@@ -21,25 +21,34 @@
         "pleaseEnterCaptcha": "Please enter captcha"
     },
     "menu": {
-        "app": "App",
-        "skills": "Skills",
+        "app": "Chat",
+        "user": "User",
+        "github": "GitHub",
+        "bookopen": "Document",
+        "skills": "Build",
         "knowledge": "Knowledge",
+        "evaluation": "Evaluation",
         "models": "Models",
         "system": "System",
+        "log": "Logs",
         "themeSwitch": "Theme Switch",
         "document": "Documentation",
         "logout": "Logout",
         "logoutDescription": "Log out",
+        "logoutContent": "Are you sure to log out",
         "forBestExperience": "For the best experience, please access this website on a PC",
-        "onlineDocumentation": "Online Documentation"
+        "onlineDocumentation": "Online Documentation",
+        "changePwd": "Password"
     },
     "system": {
         "userManagement": "User Management",
         "roleManagement": "Role Management",
+        "userGroupsM": "UserGroup Management",
         "systemConfiguration": "System Configuration",
         "username": "Username",
         "confirmDisable": "Confirm disabling this user?",
-        "roleSelect": "Select Role",
+        "roleSelect": "Select Roles",
+        "userGroupsSel": "Select UserGroups",
         "roleList": "Role List",
         "confirmText": "Are you sure you want to delete",
         "roleName": "Role Name",
@@ -52,8 +61,41 @@
         "roleNamePrompt": "Role name cannot exceed 50 characters",
         "roleNameRequired": "Role name is required",
         "roleNameExists": "Role name already exists",
+        "groupNameExists": "UserGroup name already exists",
+        "groupNamePrompt": "UserGroup name cannot exceed 30 characters",
+        "groupNameRequired": "UserGroup name is required",
         "parameterConfig": "Parameter Configuration",
-        "language": "Language"
+        "language": "Language",
+        "assistantAuthorization": "Assistant Authorization",
+        "assistantName": "Assistant Name",
+        "userList": "User List",
+        "userGroupList": "UserGroup List",
+        "userGroup": "userGroup",
+        "role": "Role",
+        "searchUserGroups": "Search user groups",
+        "searchRoles": "Search roles",
+        "reset": "Reset",
+        "confirm": "Confirm",
+        "userGroupName": "Enter UserGroup Name",
+        "groupName": "UserGroup Name",
+        "admins": "Admin",
+        "flowControl": "Overall UserGroup Flow Control",
+        "AssistantFlowCtrl": "Assistant Flow Control",
+        "SkillFlowCtrl": "Skill Flow Control",
+        "createdBy": "CreatedBy",
+        "flowCtrlStrategy": "Flow control strategy",
+        "limit": "Limit",
+        "unlimited": "Unlimited",
+        "iconHover": "Simultaneously constrained by the overall traffic control strategy of the user group",
+        "maximum": "Up to",
+        "perMinute": "simultaneous online sessions",
+        "changeTime": "Modification Time",
+        "deleteGroup": "After deletion 【{{name}}】 will no longer exist, Do you want to delete it?",
+        "currentGroup": "Current UserGroup",
+        "defaultGroup": "DefaultGroup",
+        "resetPwd": "ResetPassword",
+        "selectGroup": "Please select a user group",
+        "selectRole": "Please select a role"
     },
     "skills": {
         "manageTemplate": "Manage Skill Templates",
@@ -303,7 +345,40 @@
         "knowledgeImg": "Knowledge Base Avatar",
         "indexModel": "Index model",
         "dataUp": "Single data upper limit",
-        "introduce": "Introduce"
+        "introduce": "Introduce",
+        "fileUploadResult": "Out of the {{total}} files uploaded, {{failed}} failed to upload.",
+        "modalTitle": "File Duplicate Prompt",
+        "modalMessage": "The following files already exist in the knowledge base. Continuing the upload will overwrite the original files and processing strategy. Do you want to proceed with overwrite?",
+        "keepOriginal": "Keep Original Files",
+        "override": "Override",
+        "toolName": "ToolName"
+    },
+    "evaluation": {
+        "id": "任务ID",
+        "filename": "测试文件名称",
+        "skillAssistant": "技能助手",
+        "status": "状态",
+        "score": "评测分数",
+        "createDate": "创建日期",
+        "download": "下载",
+        "confirmDeleteEvaluation": "确认删除该评测任务?",
+        "createTitle": "新建任务",
+        "selectLabel": "选择要评测的技能或者助手:",
+        "selectPlaceholder": "请选择",
+        "dataLabel": "测试集数据:",
+        "fileExpandName": "支持扩展名:",
+        "downloadTemplate": "下载模板文件",
+        "promptLabel": "评测指令文本:",
+        "enterExecType": "请选择要评测的技能或助手",
+        "enterUniqueId": "请选择技能或助手ID",
+        "enterVersion": "请选择技能的版本",
+        "enterFile": "请选择测试集数据",
+        "enterPrompt": "评测指令不能为空",
+        "fileSizeLimit": "文件大小限制在10M以内",
+        "evaluationCollection": "评测集合",
+        "tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
+        "create": "创建",
+        "cancel": "取消"
     },
     "code": {
         "editPythonCodeDescription": "Edit your Python code here. This code snippet accepts module imports and a function definition. Make sure your function returns a string.",
@@ -576,6 +651,41 @@
         "result": "Test Result",
         "outResultPlaceholder": "Click the button to output the result"
     },
+    "resetPassword": {
+        "slogen": "Securely Reset Your Password",
+        "currentPassword": "Current Password",
+        "newPassword": "New Password",
+        "confirmNewPassword": "Confirm New Password",
+        "pleaseEnterCurrentPassword": "Please enter your current password.",
+        "pleaseEnterNewPassword": "Please enter your new password.",
+        "pleaseEnterConfirmPassword": "Please confirm your new password.",
+        "newPasswordTooShort": "New password must be at least 8 characters.",
+        "passwordMismatch": "The new passwords do not match.",
+        "resetButton": "Change Password",
+        "passwordResetSuccess": "Your password has been successfully reset.",
+        "adminResetSuccess": "Password has been successfully reset",
+        "resetFailed": "Pwd Reset Failed",
+        "notEmpty": "The new password cannot be empty"
+    },
+    "log": {
+        "auditManagement": "Audit Management",
+        "searchButton": "Search",
+        "resetButton": "Reset",
+        "auditId": "Audit ID",
+        "username": "Username",
+        "operationTime": "Operation Time",
+        "systemModule": "System Module",
+        "operationAction": "Operation Action",
+        "objectType": "Operation Object Type",
+        "operationObject": "Operation Object",
+        "ipAddress": "IP Address",
+        "remark": "Remark",
+        "selectUser": "Select User",
+        "selectUserGroup": "Select User Group",
+        "startDate": "Start Date",
+        "endDate": "End Date",
+        "actionBehavior": "Action Behavior"
+    },
     "agents": {
         "AgentInitializer":{
             "display_name": "AgentInitializer",

+ 120 - 9
build/locales/zh/bs.json

@@ -14,32 +14,42 @@
         "pleaseEnterAccount": "请填写账号",
         "pleaseEnterPassword": "请填写密码",
         "accountTooShort": "账号过短",
-        "passwordTooShort": "请填写密码,至少6位",
+        "passwordTooShort": "请填写密码,至少7位",
         "passwordError": "密码必须包含字母、数字!",
         "passwordMismatch": "两次密码不一致",
         "registrationSuccess": "注册成功,请输入密码进行登录"
     },
     "menu": {
-        "app": "聊天",
+        "user": "用户",
+        "bookopen": "帮助文档",
+        "github": "GitHub",
+        "app": "会 话",
         "skills": "NPC",
         "knowledge": "知识库",
-        "models": "模型",
-        "system": "账号",
+        "evaluation": "评 测",
+        "models": "模 型",
+        "system": "账 号",
+        "log": "审 计",
         "themeSwitch": "主题切换",
         "document": "文档",
         "logout": "退出",
         "logoutDescription": "退出登录",
+        "logoutContent": "确认退出登录吗",
         "forBestExperience": "为了您的良好体验,请在 PC 端访问该网站",
-        "onlineDocumentation": "在线文档"
+        "onlineDocumentation": "在线文档",
+        "changePwd": "修改密码"
     },
     "system": {
-        "userManagement": "用户管理",
+       "userManagement": "用户管理",
+        "userGroupsM": "用户组管理",
         "roleManagement": "角色管理",
         "systemConfiguration": "系统配置",
         "username": "用户名",
         "confirmDisable": "确认禁用该用户?",
         "roleSelect": "角色选择",
+        "userGroupsSel": "用户组选择",
         "roleList": "角色列表",
+        "userGroupList": "用户组列表",
         "confirmText": "是否删除",
         "roleName": "角色名称",
         "skillAuthorization": "能力授权",
@@ -49,10 +59,42 @@
         "usePermission": "使用权限",
         "managePermission": "管理权限",
         "roleNamePrompt": "角色名称不能超过50字符",
-        "roleNameRequired": "角色名称不能为空",
+        "roleNameRequired": "角色名称不可为空",
+        "groupNameExists": "用户组名称不可重复",
+        "groupNamePrompt": "用户组名称不能超过30字符",
+        "groupNameRequired": "用户组名称不可为空",
         "roleNameExists": "角色名称已存在",
         "parameterConfig": "参数配置",
-        "language": "语言"
+        "language": "语言",
+        "assistantAuthorization": "NPC授权",
+        "assistantName": "NPC名称",
+        "userList": "用户列表",
+        "userGroup": "用户组",
+        "role": "角色",
+        "searchUserGroups": "搜索用户组",
+        "searchRoles": "搜索角色",
+        "reset": "重置",
+        "confirm": "确认",
+        "userGroupName": "输入用户组名称",
+        "groupName": "用户组名称",
+        "admins": "管理员",
+        "flowControl": "用户组整体流量控制",
+        "AssistantFlowCtrl": "NPC流量控制",
+        "SkillFlowCtrl": "能力流量控制",
+        "createdBy": "创建人",
+        "flowCtrlStrategy": "流量控制策略",
+        "limit": "有限制",
+        "unlimited": "无限制",
+        "iconHover": "同时受用户组整体流量控制策略约束",
+        "maximum": "最多",
+        "perMinute": "个同时在线会话",
+        "changeTime": "修改时间",
+        "deleteGroup": "删除后 【{{name}}】 将不再存在,是否删除?",
+        "currentGroup": "当前用户组",
+        "defaultGroup": "默认用户组",
+        "resetPwd": "重置密码",
+        "selectGroup": "请选择用户组",
+        "selectRole": "请选择角色"
     },
     "skills": {
         "manageTemplate": "管理能力模板",
@@ -295,7 +337,41 @@
         "knowledgeImg": "知识库头像",
         "indexModel": "索引模型",
         "dataUp": "单条数据上限",
-        "introduce": "介绍"
+        "introduce": "介绍",
+        "fileUploadResult": "共上传 {{total}} 份文件,有 {{failed}} 份文件上传失败",
+        "modalTitle": "文件重复提示",
+        "modalMessage": "以下文件在知识库中已存在,继续上传将会覆盖原有文件以及处理策略,是否覆盖?",
+        "keepOriginal": "不覆盖,保留原文件",
+        "override": "覆盖",
+        "toolName": "工具名称"
+    },
+    "evaluation": {
+        "id": "任务ID",
+        "filename": "测试文件名称",
+        "skillAssistant": "能力NPC",
+        "status": "状态",
+        "score": "评测分数",
+        "createDate": "创建日期",
+        "download": "下载",
+        "confirmDeleteEvaluation": "确认删除该评测任务?",
+        "createTitle": "新建任务",
+        "selectLabel": "选择要评测的能力或者NPC:",
+        "selectPlaceholder": "请选择",
+        "selectInputPlaceholder": "请根据名称进行搜索",
+        "dataLabel": "测试集数据:",
+        "fileExpandName": "支持扩展名:",
+        "downloadTemplate": "下载模板文件",
+        "promptLabel": "评测指令文本:",
+        "enterExecType": "请选择要评测的能力或NPC",
+        "enterUniqueId": "请选择能力或NPCID",
+        "enterVersion": "请选择能力的版本",
+        "enterFile": "请选择测试集数据",
+        "enterPrompt": "评测指令不能为空",
+        "fileSizeLimit": "文件大小限制在10M以内",
+        "evaluationCollection": "评测集合",
+        "tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
+        "create": "创建",
+        "cancel": "取消"
     },
     "code": {
         "editPythonCodeDescription": "编辑你的 Python 代码此代码片段接受模块导入和一个函数定义。确保您的函数返回一个字符串。",
@@ -568,6 +644,41 @@
         "result": "测试结果",
         "outResultPlaceholder": "点击按钮,输出结果"
     },
+    "resetPassword": {
+        "slogen": "安全地重置您的密码",
+        "currentPassword": "当前密码",
+        "newPassword": "新密码",
+        "confirmNewPassword": "确认新密码",
+        "pleaseEnterCurrentPassword": "请输入当前密码。",
+        "pleaseEnterNewPassword": "请输入新密码。",
+        "pleaseEnterConfirmPassword": "请确认新密码。",
+        "newPasswordTooShort": "新密码必须至少 7 个字符。",
+        "passwordMismatch": "新密码不匹配。",
+        "resetButton": "修改密码",
+        "passwordResetSuccess": "您的密码已成功修改",
+        "adminResetSuccess": "密码已重置",
+        "resetFailed": "密码重置失败",
+        "notEmpty": "新密码不能为空"
+    },
+    "log": {
+        "auditManagement": "审计管理",
+        "searchButton": "查询",
+        "resetButton": "重置",
+        "auditId": "审计ID",
+        "username": "用户名",
+        "operationTime": "操作时间",
+        "systemModule": "系统模块",
+        "operationAction": "操作行为",
+        "objectType": "操作对象类型",
+        "operationObject": "操作对象",
+        "ipAddress": "IP地址",
+        "remark": "备注",
+        "selectUser": "选择用户",
+        "selectUserGroup": "选择用户组",
+        "startDate": "开始日期",
+        "endDate": "结束日期",
+        "actionBehavior": "操作行为"
+    },
     "agents": {
         "AgentInitializer": {
             "display_name": "AgentInitializer",

+ 519 - 1
package-lock.json

@@ -61,7 +61,9 @@
         "react": "^18.2.0",
         "react-ace": "^10.1.0",
         "react-beautiful-dnd": "^13.1.1",
+        "react-color": "^2.19.3",
         "react-cookie": "^4.1.1",
+        "react-day-picker": "^8.10.1",
         "react-dom": "^18.2.0",
         "react-dropzone": "^14.2.3",
         "react-error-boundary": "^4.0.11",
@@ -115,6 +117,7 @@
         "tailwindcss": "^3.3.3",
         "typescript": "^5.2.2",
         "vite": "^4.5.2",
+        "vite-plugin-html": "^3.2.2",
         "vite-plugin-static-copy": "^0.17.0"
       },
       "engines": {
@@ -1283,6 +1286,14 @@
         "react": ">= 16"
       }
     },
+    "node_modules/@icons/material": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmmirror.com/@icons/material/-/material-0.2.4.tgz",
+      "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
+      "peerDependencies": {
+        "react": "*"
+      }
+    },
     "node_modules/@isaacs/cliui": {
       "version": "8.0.2",
       "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -1401,6 +1412,16 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "devOptional": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.4.15",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
@@ -4950,6 +4971,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/async": {
+      "version": "3.2.5",
+      "resolved": "https://registry.npmmirror.com/async/-/async-3.2.5.tgz",
+      "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
+      "dev": true
+    },
     "node_modules/async-validator": {
       "version": "4.2.5",
       "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
@@ -5284,6 +5311,12 @@
         "safe-buffer": "~5.2.0"
       }
     },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
     "node_modules/brace-expansion": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -5432,6 +5465,16 @@
         "node": ">=6"
       }
     },
+    "node_modules/camel-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz",
+      "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+      "dev": true,
+      "dependencies": {
+        "pascal-case": "^3.1.2",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/camelcase": {
       "version": "6.3.0",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
@@ -5615,6 +5658,27 @@
       "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
       "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
     },
+    "node_modules/clean-css": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz",
+      "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+      "dev": true,
+      "dependencies": {
+        "source-map": "~0.6.0"
+      },
+      "engines": {
+        "node": ">= 10.0"
+      }
+    },
+    "node_modules/clean-css/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/cli-cursor": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
@@ -5975,6 +6039,12 @@
       "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
       "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
     },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true
+    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -6014,7 +6084,22 @@
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
-      "optional": true
+      "devOptional": true
+    },
+    "node_modules/connect-history-api-fallback": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+      "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/consola": {
+      "version": "2.15.3",
+      "resolved": "https://registry.npmmirror.com/consola/-/consola-2.15.3.tgz",
+      "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==",
+      "dev": true
     },
     "node_modules/console-control-strings": {
       "version": "1.1.0",
@@ -6115,6 +6200,22 @@
         "tiny-invariant": "^1.0.6"
       }
     },
+    "node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
     "node_modules/css-selector-tokenizer": {
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
@@ -6124,6 +6225,18 @@
         "fastparse": "^1.1.2"
       }
     },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
     "node_modules/css.escape": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
@@ -6401,6 +6514,16 @@
         "node": ">=12"
       }
     },
+    "node_modules/date-fns": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz",
+      "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+      "peer": true,
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/kossnocorp"
+      }
+    },
     "node_modules/dayjs": {
       "version": "1.11.10",
       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
@@ -6702,6 +6825,32 @@
         "csstype": "^3.0.2"
       }
     },
+    "node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
     "node_modules/domexception": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
@@ -6714,11 +6863,40 @@
         "node": ">=12"
       }
     },
+    "node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
     "node_modules/dompurify": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.1.tgz",
       "integrity": "sha512-tVP8C/GJwnABOn/7cx/ymx/hXpmBfWIPihC1aOEvS8GbMqy3pgeYtJk1HXN3CO7tu+8bpY18f6isjR5Cymj0TQ=="
     },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
     "node_modules/dot-case": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
@@ -6728,6 +6906,27 @@
         "tslib": "^2.0.3"
       }
     },
+    "node_modules/dotenv": {
+      "version": "16.4.5",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.5.tgz",
+      "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz",
+      "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/duplexer2": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -6778,6 +6977,21 @@
       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
       "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
     },
+    "node_modules/ejs": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
+      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+      "dev": true,
+      "dependencies": {
+        "jake": "^10.8.5"
+      },
+      "bin": {
+        "ejs": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.4.750",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.750.tgz",
@@ -7177,6 +7391,27 @@
         "url": "https://github.com/sindresorhus/file-type?sponsor=1"
       }
     },
+    "node_modules/filelist": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz",
+      "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+      "dev": true,
+      "dependencies": {
+        "minimatch": "^5.0.1"
+      }
+    },
+    "node_modules/filelist/node_modules/minimatch": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
+      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/filename-reserved-regex": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz",
@@ -8168,6 +8403,15 @@
         "node": ">=0.4"
       }
     },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
     "node_modules/highlight.js": {
       "version": "10.7.3",
       "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
@@ -8200,6 +8444,36 @@
         "node": ">=12"
       }
     },
+    "node_modules/html-minifier-terser": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+      "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+      "dev": true,
+      "dependencies": {
+        "camel-case": "^4.1.2",
+        "clean-css": "^5.2.2",
+        "commander": "^8.3.0",
+        "he": "^1.2.0",
+        "param-case": "^3.0.4",
+        "relateurl": "^0.2.7",
+        "terser": "^5.10.0"
+      },
+      "bin": {
+        "html-minifier-terser": "cli.js"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/html-minifier-terser/node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
     "node_modules/html-parse-stringify": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
@@ -8909,6 +9183,46 @@
         "@pkgjs/parseargs": "^0.11.0"
       }
     },
+    "node_modules/jake": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.2.tgz",
+      "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
+      "dev": true,
+      "dependencies": {
+        "async": "^3.2.3",
+        "chalk": "^4.0.2",
+        "filelist": "^1.0.4",
+        "minimatch": "^3.1.2"
+      },
+      "bin": {
+        "jake": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jake/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/jake/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/jest-diff": {
       "version": "27.5.1",
       "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz",
@@ -9432,6 +9746,11 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/material-colors": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmmirror.com/material-colors/-/material-colors-1.2.6.tgz",
+      "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
+    },
     "node_modules/mathjax-full": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz",
@@ -11789,6 +12108,16 @@
         }
       }
     },
+    "node_modules/node-html-parser": {
+      "version": "5.4.2",
+      "resolved": "https://registry.npmmirror.com/node-html-parser/-/node-html-parser-5.4.2.tgz",
+      "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==",
+      "dev": true,
+      "dependencies": {
+        "css-select": "^4.2.1",
+        "he": "1.2.0"
+      }
+    },
     "node_modules/node-releases": {
       "version": "2.0.14",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -11862,6 +12191,18 @@
         "set-blocking": "^2.0.0"
       }
     },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
     "node_modules/nwsapi": {
       "version": "2.2.9",
       "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz",
@@ -12080,6 +12421,16 @@
         "node": ">=4"
       }
     },
+    "node_modules/param-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz",
+      "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+      "dev": true,
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/parent-module": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -12147,6 +12498,16 @@
         "url": "https://github.com/fb55/entities?sponsor=1"
       }
     },
+    "node_modules/pascal-case": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz",
+      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+      "dev": true,
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/path-browserify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -12215,6 +12576,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/pathe": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/pathe/-/pathe-0.2.0.tgz",
+      "integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==",
+      "dev": true
+    },
     "node_modules/pdfjs-dist": {
       "version": "3.10.111",
       "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.10.111.tgz",
@@ -13432,6 +13799,23 @@
         "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
       }
     },
+    "node_modules/react-color": {
+      "version": "2.19.3",
+      "resolved": "https://registry.npmmirror.com/react-color/-/react-color-2.19.3.tgz",
+      "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
+      "dependencies": {
+        "@icons/material": "^0.2.4",
+        "lodash": "^4.17.15",
+        "lodash-es": "^4.17.15",
+        "material-colors": "^1.2.1",
+        "prop-types": "^15.5.10",
+        "reactcss": "^1.2.0",
+        "tinycolor2": "^1.4.1"
+      },
+      "peerDependencies": {
+        "react": "*"
+      }
+    },
     "node_modules/react-colorful": {
       "version": "5.6.1",
       "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
@@ -13454,6 +13838,19 @@
         "react": ">= 16.3.0"
       }
     },
+    "node_modules/react-day-picker": {
+      "version": "8.10.1",
+      "resolved": "https://registry.npmmirror.com/react-day-picker/-/react-day-picker-8.10.1.tgz",
+      "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==",
+      "funding": {
+        "type": "individual",
+        "url": "https://github.com/sponsors/gpbl"
+      },
+      "peerDependencies": {
+        "date-fns": "^2.28.0 || ^3.0.0",
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/react-dom": {
       "version": "18.3.1",
       "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@@ -13859,6 +14256,14 @@
         "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
       }
     },
+    "node_modules/reactcss": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
+      "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
+      "dependencies": {
+        "lodash": "^4.0.1"
+      }
+    },
     "node_modules/reactflow": {
       "version": "11.11.2",
       "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.2.tgz",
@@ -14149,6 +14554,15 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/relateurl": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
     "node_modules/remark-breaks": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz",
@@ -15308,6 +15722,31 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "devOptional": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/source-map-support/node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "devOptional": true
+    },
+    "node_modules/source-map-support/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/space-separated-tokens": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
@@ -15791,6 +16230,30 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "optional": true
     },
+    "node_modules/terser": {
+      "version": "5.31.3",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.31.3.tgz",
+      "integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==",
+      "devOptional": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "devOptional": true
+    },
     "node_modules/thenify": {
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -15845,6 +16308,11 @@
       "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
       "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
     },
+    "node_modules/tinycolor2": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
+    },
     "node_modules/to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -16487,6 +16955,56 @@
         }
       }
     },
+    "node_modules/vite-plugin-html": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/vite-plugin-html/-/vite-plugin-html-3.2.2.tgz",
+      "integrity": "sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==",
+      "dev": true,
+      "dependencies": {
+        "@rollup/pluginutils": "^4.2.0",
+        "colorette": "^2.0.16",
+        "connect-history-api-fallback": "^1.6.0",
+        "consola": "^2.15.3",
+        "dotenv": "^16.0.0",
+        "dotenv-expand": "^8.0.2",
+        "ejs": "^3.1.6",
+        "fast-glob": "^3.2.11",
+        "fs-extra": "^10.0.1",
+        "html-minifier-terser": "^6.1.0",
+        "node-html-parser": "^5.3.3",
+        "pathe": "^0.2.0"
+      },
+      "peerDependencies": {
+        "vite": ">=2.0.0"
+      }
+    },
+    "node_modules/vite-plugin-html/node_modules/@rollup/pluginutils": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+      "dev": true,
+      "dependencies": {
+        "estree-walker": "^2.0.1",
+        "picomatch": "^2.2.2"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
+    "node_modules/vite-plugin-html/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/vite-plugin-static-copy": {
       "version": "0.17.1",
       "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.17.1.tgz",

+ 3 - 0
package.json

@@ -56,7 +56,9 @@
     "react": "^18.2.0",
     "react-ace": "^10.1.0",
     "react-beautiful-dnd": "^13.1.1",
+    "react-color": "^2.19.3",
     "react-cookie": "^4.1.1",
+    "react-day-picker": "^8.10.1",
     "react-dom": "^18.2.0",
     "react-dropzone": "^14.2.3",
     "react-error-boundary": "^4.0.11",
@@ -136,6 +138,7 @@
     "tailwindcss": "^3.3.3",
     "typescript": "^5.2.2",
     "vite": "^4.5.2",
+    "vite-plugin-html": "^3.2.2",
     "vite-plugin-static-copy": "^0.17.0"
   },
   "engines": {

+ 116 - 6
public/locales/en/bs.json

@@ -21,25 +21,34 @@
         "pleaseEnterCaptcha": "Please enter captcha"
     },
     "menu": {
-        "app": "App",
-        "skills": "Skills",
+        "app": "Chat",
+        "user": "User",
+        "github": "GitHub",
+        "bookopen": "Document",
+        "skills": "Build",
         "knowledge": "Knowledge",
+        "evaluation": "Evaluation",
         "models": "Models",
         "system": "System",
+        "log": "Logs",
         "themeSwitch": "Theme Switch",
         "document": "Documentation",
         "logout": "Logout",
         "logoutDescription": "Log out",
+        "logoutContent": "Are you sure to log out",
         "forBestExperience": "For the best experience, please access this website on a PC",
-        "onlineDocumentation": "Online Documentation"
+        "onlineDocumentation": "Online Documentation",
+        "changePwd": "Password"
     },
     "system": {
         "userManagement": "User Management",
         "roleManagement": "Role Management",
+        "userGroupsM": "UserGroup Management",
         "systemConfiguration": "System Configuration",
         "username": "Username",
         "confirmDisable": "Confirm disabling this user?",
-        "roleSelect": "Select Role",
+        "roleSelect": "Select Roles",
+        "userGroupsSel": "Select UserGroups",
         "roleList": "Role List",
         "confirmText": "Are you sure you want to delete",
         "roleName": "Role Name",
@@ -52,8 +61,41 @@
         "roleNamePrompt": "Role name cannot exceed 50 characters",
         "roleNameRequired": "Role name is required",
         "roleNameExists": "Role name already exists",
+        "groupNameExists": "UserGroup name already exists",
+        "groupNamePrompt": "UserGroup name cannot exceed 30 characters",
+        "groupNameRequired": "UserGroup name is required",
         "parameterConfig": "Parameter Configuration",
-        "language": "Language"
+        "language": "Language",
+        "assistantAuthorization": "Assistant Authorization",
+        "assistantName": "Assistant Name",
+        "userList": "User List",
+        "userGroupList": "UserGroup List",
+        "userGroup": "userGroup",
+        "role": "Role",
+        "searchUserGroups": "Search user groups",
+        "searchRoles": "Search roles",
+        "reset": "Reset",
+        "confirm": "Confirm",
+        "userGroupName": "Enter UserGroup Name",
+        "groupName": "UserGroup Name",
+        "admins": "Admin",
+        "flowControl": "Overall UserGroup Flow Control",
+        "AssistantFlowCtrl": "Assistant Flow Control",
+        "SkillFlowCtrl": "Skill Flow Control",
+        "createdBy": "CreatedBy",
+        "flowCtrlStrategy": "Flow control strategy",
+        "limit": "Limit",
+        "unlimited": "Unlimited",
+        "iconHover": "Simultaneously constrained by the overall traffic control strategy of the user group",
+        "maximum": "Up to",
+        "perMinute": "simultaneous online sessions",
+        "changeTime": "Modification Time",
+        "deleteGroup": "After deletion 【{{name}}】 will no longer exist, Do you want to delete it?",
+        "currentGroup": "Current UserGroup",
+        "defaultGroup": "DefaultGroup",
+        "resetPwd": "ResetPassword",
+        "selectGroup": "Please select a user group",
+        "selectRole": "Please select a role"
     },
     "skills": {
         "manageTemplate": "Manage Skill Templates",
@@ -303,7 +345,40 @@
         "knowledgeImg": "Knowledge Base Avatar",
         "indexModel": "Index model",
         "dataUp": "Single data upper limit",
-        "introduce": "Introduce"
+        "introduce": "Introduce",
+        "fileUploadResult": "Out of the {{total}} files uploaded, {{failed}} failed to upload.",
+        "modalTitle": "File Duplicate Prompt",
+        "modalMessage": "The following files already exist in the knowledge base. Continuing the upload will overwrite the original files and processing strategy. Do you want to proceed with overwrite?",
+        "keepOriginal": "Keep Original Files",
+        "override": "Override",
+        "toolName": "ToolName"
+    },
+    "evaluation": {
+        "id": "任务ID",
+        "filename": "测试文件名称",
+        "skillAssistant": "技能助手",
+        "status": "状态",
+        "score": "评测分数",
+        "createDate": "创建日期",
+        "download": "下载",
+        "confirmDeleteEvaluation": "确认删除该评测任务?",
+        "createTitle": "新建任务",
+        "selectLabel": "选择要评测的技能或者助手:",
+        "selectPlaceholder": "请选择",
+        "dataLabel": "测试集数据:",
+        "fileExpandName": "支持扩展名:",
+        "downloadTemplate": "下载模板文件",
+        "promptLabel": "评测指令文本:",
+        "enterExecType": "请选择要评测的技能或助手",
+        "enterUniqueId": "请选择技能或助手ID",
+        "enterVersion": "请选择技能的版本",
+        "enterFile": "请选择测试集数据",
+        "enterPrompt": "评测指令不能为空",
+        "fileSizeLimit": "文件大小限制在10M以内",
+        "evaluationCollection": "评测集合",
+        "tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
+        "create": "创建",
+        "cancel": "取消"
     },
     "code": {
         "editPythonCodeDescription": "Edit your Python code here. This code snippet accepts module imports and a function definition. Make sure your function returns a string.",
@@ -576,6 +651,41 @@
         "result": "Test Result",
         "outResultPlaceholder": "Click the button to output the result"
     },
+    "resetPassword": {
+        "slogen": "Securely Reset Your Password",
+        "currentPassword": "Current Password",
+        "newPassword": "New Password",
+        "confirmNewPassword": "Confirm New Password",
+        "pleaseEnterCurrentPassword": "Please enter your current password.",
+        "pleaseEnterNewPassword": "Please enter your new password.",
+        "pleaseEnterConfirmPassword": "Please confirm your new password.",
+        "newPasswordTooShort": "New password must be at least 8 characters.",
+        "passwordMismatch": "The new passwords do not match.",
+        "resetButton": "Change Password",
+        "passwordResetSuccess": "Your password has been successfully reset.",
+        "adminResetSuccess": "Password has been successfully reset",
+        "resetFailed": "Pwd Reset Failed",
+        "notEmpty": "The new password cannot be empty"
+    },
+    "log": {
+        "auditManagement": "Audit Management",
+        "searchButton": "Search",
+        "resetButton": "Reset",
+        "auditId": "Audit ID",
+        "username": "Username",
+        "operationTime": "Operation Time",
+        "systemModule": "System Module",
+        "operationAction": "Operation Action",
+        "objectType": "Operation Object Type",
+        "operationObject": "Operation Object",
+        "ipAddress": "IP Address",
+        "remark": "Remark",
+        "selectUser": "Select User",
+        "selectUserGroup": "Select User Group",
+        "startDate": "Start Date",
+        "endDate": "End Date",
+        "actionBehavior": "Action Behavior"
+    },
     "agents": {
         "AgentInitializer":{
             "display_name": "AgentInitializer",

+ 120 - 9
public/locales/zh/bs.json

@@ -14,32 +14,42 @@
         "pleaseEnterAccount": "请填写账号",
         "pleaseEnterPassword": "请填写密码",
         "accountTooShort": "账号过短",
-        "passwordTooShort": "请填写密码,至少6位",
+        "passwordTooShort": "请填写密码,至少7位",
         "passwordError": "密码必须包含字母、数字!",
         "passwordMismatch": "两次密码不一致",
         "registrationSuccess": "注册成功,请输入密码进行登录"
     },
     "menu": {
-        "app": "聊天",
+        "user": "用户",
+        "bookopen": "帮助文档",
+        "github": "GitHub",
+        "app": "会 话",
         "skills": "NPC",
         "knowledge": "知识库",
-        "models": "模型",
-        "system": "账号",
+        "evaluation": "评 测",
+        "models": "模 型",
+        "system": "账 号",
+        "log": "审 计",
         "themeSwitch": "主题切换",
         "document": "文档",
         "logout": "退出",
         "logoutDescription": "退出登录",
+        "logoutContent": "确认退出登录吗",
         "forBestExperience": "为了您的良好体验,请在 PC 端访问该网站",
-        "onlineDocumentation": "在线文档"
+        "onlineDocumentation": "在线文档",
+        "changePwd": "修改密码"
     },
     "system": {
-        "userManagement": "用户管理",
+       "userManagement": "用户管理",
+        "userGroupsM": "用户组管理",
         "roleManagement": "角色管理",
         "systemConfiguration": "系统配置",
         "username": "用户名",
         "confirmDisable": "确认禁用该用户?",
         "roleSelect": "角色选择",
+        "userGroupsSel": "用户组选择",
         "roleList": "角色列表",
+        "userGroupList": "用户组列表",
         "confirmText": "是否删除",
         "roleName": "角色名称",
         "skillAuthorization": "能力授权",
@@ -49,10 +59,42 @@
         "usePermission": "使用权限",
         "managePermission": "管理权限",
         "roleNamePrompt": "角色名称不能超过50字符",
-        "roleNameRequired": "角色名称不能为空",
+        "roleNameRequired": "角色名称不可为空",
+        "groupNameExists": "用户组名称不可重复",
+        "groupNamePrompt": "用户组名称不能超过30字符",
+        "groupNameRequired": "用户组名称不可为空",
         "roleNameExists": "角色名称已存在",
         "parameterConfig": "参数配置",
-        "language": "语言"
+        "language": "语言",
+        "assistantAuthorization": "NPC授权",
+        "assistantName": "NPC名称",
+        "userList": "用户列表",
+        "userGroup": "用户组",
+        "role": "角色",
+        "searchUserGroups": "搜索用户组",
+        "searchRoles": "搜索角色",
+        "reset": "重置",
+        "confirm": "确认",
+        "userGroupName": "输入用户组名称",
+        "groupName": "用户组名称",
+        "admins": "管理员",
+        "flowControl": "用户组整体流量控制",
+        "AssistantFlowCtrl": "NPC流量控制",
+        "SkillFlowCtrl": "能力流量控制",
+        "createdBy": "创建人",
+        "flowCtrlStrategy": "流量控制策略",
+        "limit": "有限制",
+        "unlimited": "无限制",
+        "iconHover": "同时受用户组整体流量控制策略约束",
+        "maximum": "最多",
+        "perMinute": "个同时在线会话",
+        "changeTime": "修改时间",
+        "deleteGroup": "删除后 【{{name}}】 将不再存在,是否删除?",
+        "currentGroup": "当前用户组",
+        "defaultGroup": "默认用户组",
+        "resetPwd": "重置密码",
+        "selectGroup": "请选择用户组",
+        "selectRole": "请选择角色"
     },
     "skills": {
         "manageTemplate": "管理能力模板",
@@ -295,7 +337,41 @@
         "knowledgeImg": "知识库头像",
         "indexModel": "索引模型",
         "dataUp": "单条数据上限",
-        "introduce": "介绍"
+        "introduce": "介绍",
+        "fileUploadResult": "共上传 {{total}} 份文件,有 {{failed}} 份文件上传失败",
+        "modalTitle": "文件重复提示",
+        "modalMessage": "以下文件在知识库中已存在,继续上传将会覆盖原有文件以及处理策略,是否覆盖?",
+        "keepOriginal": "不覆盖,保留原文件",
+        "override": "覆盖",
+        "toolName": "工具名称"
+    },
+    "evaluation": {
+        "id": "任务ID",
+        "filename": "测试文件名称",
+        "skillAssistant": "能力NPC",
+        "status": "状态",
+        "score": "评测分数",
+        "createDate": "创建日期",
+        "download": "下载",
+        "confirmDeleteEvaluation": "确认删除该评测任务?",
+        "createTitle": "新建任务",
+        "selectLabel": "选择要评测的能力或者NPC:",
+        "selectPlaceholder": "请选择",
+        "selectInputPlaceholder": "请根据名称进行搜索",
+        "dataLabel": "测试集数据:",
+        "fileExpandName": "支持扩展名:",
+        "downloadTemplate": "下载模板文件",
+        "promptLabel": "评测指令文本:",
+        "enterExecType": "请选择要评测的能力或NPC",
+        "enterUniqueId": "请选择能力或NPCID",
+        "enterVersion": "请选择能力的版本",
+        "enterFile": "请选择测试集数据",
+        "enterPrompt": "评测指令不能为空",
+        "fileSizeLimit": "文件大小限制在10M以内",
+        "evaluationCollection": "评测集合",
+        "tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
+        "create": "创建",
+        "cancel": "取消"
     },
     "code": {
         "editPythonCodeDescription": "编辑你的 Python 代码此代码片段接受模块导入和一个函数定义。确保您的函数返回一个字符串。",
@@ -568,6 +644,41 @@
         "result": "测试结果",
         "outResultPlaceholder": "点击按钮,输出结果"
     },
+    "resetPassword": {
+        "slogen": "安全地重置您的密码",
+        "currentPassword": "当前密码",
+        "newPassword": "新密码",
+        "confirmNewPassword": "确认新密码",
+        "pleaseEnterCurrentPassword": "请输入当前密码。",
+        "pleaseEnterNewPassword": "请输入新密码。",
+        "pleaseEnterConfirmPassword": "请确认新密码。",
+        "newPasswordTooShort": "新密码必须至少 7 个字符。",
+        "passwordMismatch": "新密码不匹配。",
+        "resetButton": "修改密码",
+        "passwordResetSuccess": "您的密码已成功修改",
+        "adminResetSuccess": "密码已重置",
+        "resetFailed": "密码重置失败",
+        "notEmpty": "新密码不能为空"
+    },
+    "log": {
+        "auditManagement": "审计管理",
+        "searchButton": "查询",
+        "resetButton": "重置",
+        "auditId": "审计ID",
+        "username": "用户名",
+        "operationTime": "操作时间",
+        "systemModule": "系统模块",
+        "operationAction": "操作行为",
+        "objectType": "操作对象类型",
+        "operationObject": "操作对象",
+        "ipAddress": "IP地址",
+        "remark": "备注",
+        "selectUser": "选择用户",
+        "selectUserGroup": "选择用户组",
+        "startDate": "开始日期",
+        "endDate": "结束日期",
+        "actionBehavior": "操作行为"
+    },
     "agents": {
         "AgentInitializer": {
             "display_name": "AgentInitializer",

BIN
src/.DS_Store


+ 62 - 68
src/CustomNodes/GenericNode/components/parameterComponent/index.tsx

@@ -34,7 +34,7 @@ import {
   nodeIconsLucide,
   nodeIMgsLucide
 } from "../../../../utils";
-import { undoRedoContext } from "../../../../contexts/undoRedoContext";
+import KnowledgeSelect from "@/components/bs-comp/selectComponent/knowledge";
 
 export default function ParameterComponent({
   left,
@@ -61,7 +61,7 @@ export default function ParameterComponent({
   const updateNodeInternals = useUpdateNodeInternals();
   const [position, setPosition] = useState(0);
   const { closePopUp } = useContext(PopUpContext);
-  const { setTabsState, flow, setFlow } = useContext(TabsContext);
+  const { setTabsState, flow, version } = useContext(TabsContext);
 
   const groupedEdge = useRef(null); // 用yu过滤菜单的数据
 
@@ -91,7 +91,7 @@ export default function ParameterComponent({
   }, [id, data, reactFlowInstance])
   // milvus 组件,知识库不为空是 embbeding取消必填限制
   useEffect(() => {
-    const {embedding, index_name, collection_name, connection_args} = data.node.template
+    const { embedding, index_name, collection_name, connection_args } = data.node.template
     if ((index_name || collection_name) && embedding) {
       const hidden = disabled ? false : !!(collection_name || index_name).value
       data.node.template.embedding.required = !hidden
@@ -101,7 +101,7 @@ export default function ParameterComponent({
     }
   }, [data, disabled])
   const handleRemoveMilvusEmbeddingEdge = (nodeId) => {
-    const edges = reactFlowInstance.getEdges().filter(edge => edge.targetHandle.indexOf('Embeddings|embedding|'+nodeId) === -1)
+    const edges = reactFlowInstance.getEdges().filter(edge => edge.targetHandle.indexOf('Embeddings|embedding|' + nodeId) === -1)
     reactFlowInstance.setEdges(edges)
   }
   const [myData, setMyData] = useState(useContext(typesContext).data);
@@ -219,7 +219,6 @@ export default function ParameterComponent({
                   }}
                 /> */}
                 <img src={Icon} className="w-[14px]" alt="" />
-
               </div>
               <span className="ps-2 text-xs text-foreground w-[400px]">
                 {getNodeNames()[item.family] ?? "Other"}{" "}
@@ -268,34 +267,6 @@ export default function ParameterComponent({
     }
   }, [tooltipTitle]);
 
-
-  // 记录快照
-  const {takeSnapshot} = useContext(undoRedoContext);
-  const { types, deleteNode } = useContext(typesContext);
-//   const onNodeDragStart: NodeDragHandler = useCallback(() => {
-//     // 👇 make dragging a node undoable
-//     takeSnapshot();
-//     // 👉 you can place your event handlers here
-// }, [takeSnapshot]);
-  const onMouseDownColor = useCallback(() => {
-    // console.log(nodeColorsP)
-    // console.log(data,color,nodeColorsP,nodeColorsP[types[data.type]]);
-    // const type = types[data.type];
-    // Object.keys(nodeColors).forEach(element => {
-    //   if(element != type){
-    //     nodeColorsP[element] = "#000000"
-    //   }
-    // });
-    // console.log(nodeColors);
-    // takeSnapshot();
-
-    // data.node.display_name = "1";
-    // console.log(flow)
-  },[takeSnapshot]);
-  // useEffect(() => {
-  //   takeSnapshot();
-  // }, [nodeColors, nodeColorsP]);
-
   return (
     <div
       ref={ref}
@@ -330,37 +301,36 @@ export default function ParameterComponent({
             type === "int" ||
             type === "variable" ||
             type === "button" ||
+            type === "knowledge_one" ||
+            type === "knowledge_list" ||
             type === "NestedDict" ||
             type === "dict") &&
-          !optionalHandle ? (
-          <></>
-        ) : (
-          <ShadTooltip
-            styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
-            delayDuration={0}
-            content={refHtml.current}
-            side={left ? "left" : "right"}
-          >
-            <Handle
-              type={left ? "target" : "source"}
-              position={left ? Position.Left : Position.Right}
-              id={id}
-              isValidConnection={(connection) =>
-                isValidConnection(connection, reactFlowInstance)
-              }
-              onConnect={(params) => console.log('handle onConnect', params)}
-              className={classNames(
-                left ? "-ml-0.5 " : "-mr-0.5 ",
-                "h-3 w-3 rounded-full border-2 bg-background"
-              )}
-              onMouseDown={onMouseDownColor}
-              style={{
-                borderColor: color,
-                top: position,
-              }}
-            ></Handle>
-          </ShadTooltip>
-        )}
+          !optionalHandle ? (<></>)
+          : (
+            <ShadTooltip
+              styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
+              delayDuration={0}
+              content={refHtml.current}
+              side={left ? "left" : "right"}
+            >
+              <Handle
+                type={left ? "target" : "source"}
+                position={left ? Position.Left : Position.Right}
+                id={id}
+                isValidConnection={(connection) =>
+                  isValidConnection(connection, reactFlowInstance)
+                }
+                className={classNames(
+                  left ? "-ml-0.5 " : "-mr-0.5 ",
+                  "h-3 w-3 rounded-full border-2 bg-background"
+                )}
+                style={{
+                  borderColor: color,
+                  top: position,
+                }}
+              ></Handle>
+            </ShadTooltip>
+          )}
 
         {/* 左侧input输入项 */}
         {!data.node.template[name] ? null : left === true &&
@@ -390,10 +360,6 @@ export default function ParameterComponent({
             ) : ['index_name', 'collection_name'].includes(name) ? (
               // 知识库选择
               <CollectionNameComponent
-                setNodeClass={(nodeClass) => {
-                  data.node = nodeClass;
-                }}
-                nodeClass={data.node}
                 disabled={disabled}
                 id={data.node.template[name].collection_id ?? ""}
                 value={data.node.template[name].value ?? ""}
@@ -410,6 +376,35 @@ export default function ParameterComponent({
               />
             )}
           </div>
+        ) : left === true && type === "knowledge_one" ? (
+          // 单选知识库
+          <div className="mt-2 w-full">
+            <CollectionNameComponent
+              disabled={disabled}
+              id={data.node.template[name].collection_id ?? ""}
+              value={data.node.template[name].value ?? ""}
+              onSelect={(val, id) => { handleOnNewLibValue(val, id); val && handleRemoveMilvusEmbeddingEdge(data.id) }}
+              onChange={() => { }}
+            />
+          </div>
+        ) : left === true && type === "knowledge_list" ? (
+          // 多选知识库
+          <div className="mt-2 w-full">
+            <KnowledgeSelect
+              multiple
+              disabled={disabled}
+              value={data.node.template[name].value?.map?.((item) => ({
+                label: item.value,
+                value: item.key,
+              })) || []}
+              onChange={(vals) => {
+                handleOnNewValue(vals.map(v => ({
+                  key: v.value,
+                  value: v.label
+                })))
+              }}
+            />
+          </div>
         ) : left === true && type === "bool" ? (
           <div className="mt-2 w-full">
             {/* switch */}
@@ -480,7 +475,6 @@ export default function ParameterComponent({
             <PromptAreaComponent
               field_name={name}
               setNodeClass={(nodeClass, code) => {
-                console.log(nodeClass)
                 if (reactFlowInstance) {
                   reactFlowInstance.setNodes((nds) =>
                     nds.map((nd) => {
@@ -548,7 +542,7 @@ export default function ParameterComponent({
           </div>
         ) : left === true && type === "variable" ? (
           <div className="mt-2 w-full">
-            <VariablesComponent nodeId={data.id} flowId={flowId} onChange={(newValue) => {
+            <VariablesComponent vid={version?.id} nodeId={data.id} flowId={flowId} onChange={(newValue) => {
               data.node!.template[name].value = newValue;
               handleOnNewValue(newValue);
             }} />

BIN
src/assets/.DS_Store


BIN
src/assets/Login/.DS_Store


BIN
src/assets/Login/reset.png


BIN
src/assets/nav/.DS_Store


BIN
src/assets/nav/admin.png


BIN
src/assets/nav/admin1.png


BIN
src/assets/nav/icon1-active.png


BIN
src/assets/nav/icon1.png


BIN
src/assets/nav/icon2-active.png


BIN
src/assets/nav/icon2.png


BIN
src/assets/nav/icon3-active.png


BIN
src/assets/nav/icon3.png


BIN
src/assets/nav/icon4-active.png


BIN
src/assets/nav/icon4.png


BIN
src/assets/nav/icon5-active.png


BIN
src/assets/nav/icon5.png


BIN
src/assets/nav/icon6-active.png


BIN
src/assets/nav/icon6.png


BIN
src/assets/nav/icon7-active.png


BIN
src/assets/nav/icon7.png


BIN
src/assets/nav/mima.png


BIN
src/assets/nav/tuichu.png


BIN
src/components/.DS_Store


+ 7 - 4
src/components/VariablesComponent/index.tsx

@@ -17,7 +17,8 @@ import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
  * 通过子组件VarDialog编辑每一项
  */
 
-export default function VariablesComponent({ nodeId, flowId, onChange }: {
+export default function VariablesComponent({vid, nodeId, flowId, onChange }: {
+    vid: number
     nodeId: string
     flowId: string
     onChange: (val: any) => void
@@ -27,11 +28,12 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
 
     useEffect(() => {
         // api nodeId -> items
-        flowId && getVariablesApi({
+        flowId && vid && getVariablesApi({
+            version_id: vid,
             flow_id: flowId,
             node_id: nodeId
         }).then(arr => setItems(arr))
-    }, [flowId])
+    }, [flowId, vid])
 
     const { openPopUp, closePopUp } = useContext(PopUpContext);
     const { setErrorData } = useContext(alertContext);
@@ -60,6 +62,7 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
         const param: any = {
             "flow_id": flowId,
             "node_id": nodeId,
+            version_id: vid,
             "variable_name": _item.name,
             "value_type": Number(_item.type === VariableType.Select) + 1,
             "value": _item.type === VariableType.Text ? _item.maxLength : _item.options.map(el => el.value).join(',')
@@ -68,7 +71,7 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
             param.id = _item.id
         }
         captureAndAlertRequestErrorHoc(saveVariableApi(param).then(res => {
-            const _items = items.map(item => item.id === _item.id ? { ..._item, id: res.id } : item)
+            const _items = items.map(item => item.id === _item.id ? { ..._item, id: res.id, update: true } : item)
             // const hasValue = _items.find(item => item.name)
             // 保存时 id传出去保存,用来校验必填项
             onChange(_items.map(el => el.name))

BIN
src/components/bs-comp/.DS_Store


+ 83 - 53
src/components/bs-comp/chatComponent/ChatInput.tsx

@@ -1,16 +1,18 @@
+import { ClearIcon } from "@/components/bs-icons/clear";
 import { FormIcon } from "@/components/bs-icons/form";
 import { SendIcon } from "@/components/bs-icons/send";
+import { Button } from "@/components/bs-ui/button";
 import { Textarea } from "@/components/bs-ui/input";
 import { useToast } from "@/components/bs-ui/toast/use-toast";
 import { locationContext } from "@/contexts/locationContext";
+import { PauseIcon } from "@radix-ui/react-icons";
 import { useContext, useEffect, useRef, useState } from "react";
 import { useTranslation } from "react-i18next";
-import { useMessageStore } from "./messageStore";
 import GuideQuestions from "./GuideQuestions";
-import { ClearIcon } from "@/components/bs-icons/clear";
+import { useMessageStore } from "./messageStore";
+import { formatDate } from "@/util/utils";
+import { StopIcon } from "@radix-ui/react-icons";
 import duihua_send from "../../../assets/chat/duihua-send.png";
-import { Button } from "@/components/bs-ui/button";
-import { StopCircle } from "lucide-react";
 
 export default function ChatInput({ clear, form, questions, inputForm, wsUrl, onBeforSend }) {
     const { toast } = useToast()
@@ -21,11 +23,15 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
     const [showWhenLocked, setShowWhenLocked] = useState(false) // 强制开启表单按钮,不限制于input锁定
     const [inputLock, setInputLock] = useState({ locked: false, reason: '' })
 
-    const { messages, chatId, createSendMsg, createWsMsg, updateCurrentMessage, destory, setShowGuideQuestion } = useMessageStore()
+    const { messages, hisMessages, chatId, createSendMsg, createWsMsg, updateCurrentMessage, destory, setShowGuideQuestion } = useMessageStore()
     const currentChatIdRef = useRef(null)
     const inputRef = useRef(null)
+    const continueRef = useRef(false)
     // 停止状态
-    const [isStop, setIsStop] = useState(true)
+    const [stop, setStop] = useState({
+        show: false,
+        disable: false
+    })
     /**
      * 记录会话切换状态,等待消息加载完成时,控制表单在新会话自动展开
      */
@@ -36,16 +42,17 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
         if (changeChatedRef.current) {
             changeChatedRef.current = false
             // 新建的 form 技能,弹出窗口并锁定 input
-            if (form && messages.length === 0) {
+            if (form && messages.length === 0 && hisMessages.length === 0) {
                 setInputLock({ locked: true, reason: '' })
                 setFormShow(true)
                 setShowWhenLocked(true)
             }
         }
 
-    }, [messages])
+    }, [messages, hisMessages])
     useEffect(() => {
         if (!chatId) return
+        continueRef.current = false
         setInputLock({ locked: false, reason: '' })
         // console.log('message chatid', messages, form, chatId);
         setShowWhenLocked(false)
@@ -85,13 +92,14 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
         const event = new Event('input', { bubbles: true, cancelable: true });
         inputRef.current.value = ''
         inputRef.current.dispatchEvent(event); // 触发调节input高度
-        const [wsMsg, inputKey] = onBeforSend('', value)
+        const contunue = continueRef.current ? 'continue' : ''
+        continueRef.current = false
+        const [wsMsg, inputKey] = onBeforSend(contunue, value)
         // msg to store
         createSendMsg(wsMsg.inputs, inputKey)
         // 锁定 input
         setInputLock({ locked: true, reason: '' })
         await createWebSocket(chatId)
-        // console.log(wsMsg,inputKey);
         sendWsMsg(wsMsg)
 
         // 滚动聊天到底
@@ -100,15 +108,13 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
             messageDom.scrollTop = messageDom.scrollHeight;
         }
     }
-    const stop = async () => {
-        const [wsMsg] = onBeforSend('', '')
-        wsMsg.action = "stop"
-        sendWsMsg(wsMsg)
-        // console.log(wsMsg);
-        // sendWsMsg(wsMsg)
-    }
+
+    const diffRef = useRef(0)
     const sendWsMsg = async (msg) => {
         try {
+            diffRef.current = Date.now()
+            // console.log('WebSocket send: ' + diffRef.current + ' 毫秒');
+
             wsRef.current.send(JSON.stringify(msg))
         } catch (error) {
             toast({
@@ -128,14 +134,23 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
 
         return new Promise((res, rej) => {
             try {
+                let startTime = Date.now();
                 const ws = new WebSocket(`${webSocketProtocol}://${wsUrl}&chat_id=${chatId}`)
                 wsRef.current = ws
                 // websocket linsen
                 ws.onopen = () => {
+                    // 记录连接成功的时间
+                    let endTime = Date.now();
+
+                    // 计算连接建立所需的时间
+                    let connectionTime = endTime - startTime;
+
+                    // console.log('WebSocket 连接建立时间: ' + connectionTime + ' 毫秒');
                     console.log("WebSocket connection established!");
                     res('ok')
                 };
                 ws.onmessage = (event) => {
+                    // console.log(`WebSocket get: ${Date.now()} 毫秒;与send差值${Date.now() - diffRef.current}毫秒`);
                     const data = JSON.parse(event.data);
                     const errorMsg = data.category === 'error' ? data.intermediate_steps : ''
                     // 异常类型处理,提示
@@ -145,13 +160,17 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                     handleWsMessage(data)
                     // 群聊@自己时,开启input
                     if (['end', 'end_cover'].includes(data.type) && data.receiver?.is_self) {
-                        setInputLock({ locked: true, reason: '' })
+                        setInputLock({ locked: false, reason: '' })
+                        setStop({ show: false, disable: false })
+                        continueRef.current = true
                     }
                 }
                 ws.onclose = (event) => {
                     wsRef.current = null
                     console.error('链接手动断开 event :>> ', event);
-                    if ([1005, 1008].includes(event.code)) {
+                    setStop({ show: false, disable: false })
+
+                    if ([1005, 1008, 1009].includes(event.code)) {
                         console.warn('即将废弃 :>> ');
                         setInputLock({ locked: true, reason: event.reason })
                     } else {
@@ -167,8 +186,8 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 };
                 ws.onerror = (ev) => {
                     wsRef.current = null
+                    setStop({ show: false, disable: false })
                     console.error('链接异常error', ev);
-                    setIsStop(true)
                     toast({
                         title: `${t('chat.networkError')}:`,
                         variant: 'error',
@@ -189,15 +208,14 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
 
     // 接受 ws 消息
     const handleWsMessage = (data) => {
-        // console.log(data)
         if (Array.isArray(data) && data.length) return
-        if (data.type === "begin") {
-            setIsStop(false)
-        }else if (data.type === 'start') {
+        if (data.type === 'start') {
+            // 非continue时,展示stop按钮
+            !continueRef.current && setStop({ show: true, disable: false })
             createWsMsg(data)
         } else if (data.type === 'stream') {
+            //@ts-ignore
             updateCurrentMessage({
-                flow_id: data.flow_id,
                 chat_id: data.chat_id,
                 message: data.message,
                 thought: data.intermediate_steps
@@ -209,16 +227,16 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 thought: data.intermediate_steps || '',
                 messageId: data.message_id,
                 noAccess: false,
-                liked: 0
+                liked: 0,
+                update_time: formatDate(new Date(), 'yyyy-MM-ddTHH:mm:ss')
             }, data.type === 'end_cover')
         } else if (data.type === "close") {
-            setIsStop(true)
+            setStop({ show: false, disable: false })
             setInputLock({ locked: false, reason: '' })
         }
-
     }
 
-    // 监听重发消息事件
+    // 触发发送消息事件(重试、表单)
     useEffect(() => {
         const handleCustomEvent = (e) => {
             if (!showWhenLocked && inputLock.locked) return console.error('弹窗已锁定,消息无法发送')
@@ -247,12 +265,12 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
         // setInputEmpty(textarea.value.trim() === '')
     }
 
-    return <div className="absolute bottom-0 w-full  bg-[#fff] dark:bg-[#000000]">
-        <div className={`relative pt-[10px]`}>
+    return <div className="absolute bottom-0 w-full pt-1 bg-[#fff] dark:bg-[#000]">
+        <div className={`relative ${clear && 'pl-9'}`}>
             {/* form */}
             {
                 formShow && <div className="relative">
-                    <div className="absolute left-0 bottom-2 bg-[#1a1a1a] px-4 py-2 rounded-md w-[50%] min-w-80">
+                    <div className="absolute left-0 border bottom-2 bg-background-login px-4 py-2 rounded-md w-[50%] min-w-80 z-50">
                         {inputForm}
                     </div>
                 </div>
@@ -265,34 +283,51 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 onClick={handleClickGuideWord}
             />
             {/* clear */}
-            {/* <div className="flex absolute left-0 top-4 z-10">
+            <div className="flex absolute left-0 top-4 z-10">
                 {
                     clear && <div
                         className={`w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center `}
                         onClick={() => { !inputLock.locked && destory() }}
-                    ><ClearIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'} ></ClearIcon></div>
+                    ><ClearIcon className={`${!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'} dark:text-slate-50 dark:hover:bg-[#282828]`} ></ClearIcon></div>
                 }
-            </div> */}
-            {/* form */}
+            </div>
+            {/* form switch */}
             <div className="flex absolute left-3 top-4 z-10">
                 {
                     form && <div
                         className={`w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center `}
                         onClick={() => (showWhenLocked || !inputLock.locked) && setFormShow(!formShow)}
-                    ><FormIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></FormIcon></div>
+                    ><FormIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-800'}></FormIcon></div>
                 }
             </div>
             {/* send */}
             <div className="flex gap-2 absolute right-[2.5%] z-10">
-                <div
-                    id="bs-send-btn"
-                    className="w-[68px] h-[40px]  bg-[#FFD54C] cursor-pointer flex justify-center items-center"
-                    onClick={() => { !inputLock.locked && handleSendClick() }}
-                    style={{borderRadius:"20px"}}
-                >
-                    {/* <SendIcon className={inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></SendIcon> */}
-                    <img src={duihua_send} className="w-[20px]" alt="" />
-                </div>
+                {stop.show ?
+                    <div
+                        id="bs-send-btn"
+                        className="w-[68px] h-[40px]  bg-[#FFD54C] cursor-pointer flex justify-center items-center"
+                        style={{borderRadius:"20px"}} onClick={() => {
+                            if (stop.disable) return
+                            setStop({ show: true, disable: true });
+                            sendWsMsg({ "action": "stop" });
+                        }}>
+                        {/* <SendIcon className={`${inputLock.locked ? 'text-muted-foreground' : 'text-foreground'}`} /> */}
+                        {/* <StopIcon className={`mt-1 rounded-sm bg-[#000000] cursor-pointer ${stop.disable && 'bg-muted-foreground text-muted-foreground'}`}
+                        onClick={() => {
+                            if (stop.disable) return
+                            setStop({ show: true, disable: true });
+                            sendWsMsg({ "action": "stop" });
+                        }} /> */}
+                        <div className="w-[16px] h-[16px] bg-[#000000]" style={{borderRadius:"3px"}}></div>
+                    </div>
+                    : <div
+                        id="bs-send-btn"
+                        className="w-[68px] h-[40px]  bg-[#FFD54C] cursor-pointer flex justify-center items-center"
+                        onClick={() => { !inputLock.locked && handleSendClick() }} style={{borderRadius:"20px"}}>
+                        {/* <SendIcon className={`${inputLock.locked ? 'text-muted-foreground' : 'text-foreground'}`} /> */}
+                        <img src={duihua_send} className="w-[20px]" alt="" />
+                    </div>
+                }
             </div>
             {/* question */}
             <textarea
@@ -303,8 +338,8 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 disabled={inputLock.locked}
                 onInput={handleTextAreaHeight}
                 placeholder={inputLock.locked ? inputLock.reason : t('chat.inputPlaceholder')}
-                // className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
                 className="questionTextarea w-full resize-none border-none bg-transparent outline-none max-h-[160px]"
+                // className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
                 onKeyDown={(event) => {
                     if (event.key === "Enter" && !event.shiftKey) {
                         event.preventDefault();
@@ -312,12 +347,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                     }
                 }}
             ></textarea>
-            {!isStop && <div className=" absolute w-full flex justify-center bottom-32 pointer-events-none">
-                <Button className="rounded-full pointer-events-auto" variant="outline" disabled={isStop} onClick={() => { setIsStop(true); stop(); }}><StopCircle className="mr-2" />Stop</Button>
-            </div>}
-            
-            {/* <p className="w-[100%] text-center text-[#666666]">内容由AI生成,仅供参考</p> */}
         </div>
         <p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p>
     </div>
-};
+};

+ 3 - 1
src/components/bs-comp/chatComponent/FileBs.tsx

@@ -36,7 +36,9 @@ export default function FileBs({ data,flow_type }) {
         <div className="w-fit min-h-8 rounded-2xl px-6 py-4 max-w-[90%]">
             {data.sender && <p className="text-primary text-xs mb-2" style={{ background: avatarColor }}>{data.sender}</p>}
             <div className="flex gap-2 ">
-                {data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
+                {/* {data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>} */}
+                {flow_type.id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : flow_type.id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
+
                 {/* <div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div> */}
                 <div
                     className="flex gap-2 w-52 shadow-sm bg-[#1a1a1a] px-4 py-2 rounded-sm cursor-pointer"

+ 1 - 2
src/components/bs-comp/chatComponent/MessageBs.tsx

@@ -85,7 +85,6 @@ export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSou
     const handleCopyMessage = () => {
         copyText(messageRef.current)
     }
-    // console.log(data)
     const chatId = useMessageStore(state => state.chatId)
     return <div className="flex w-full py-1">
         <div className="w-[100%]">
@@ -94,7 +93,7 @@ export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSou
                 {/* {(data.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || data.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[50px]" alt=""/>}
                 {data.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[50px]" alt=""/>}
                 {(data.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && data.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && data.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[50px]" alt=""/>} */}
-                {data.flow_id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : data.flow_id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
+                {flow_type && flow_type.id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : flow_type.id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
 
                 <div ref={messageRef} className="text-sm max-w-[calc(100%-100px)]">
                     {/* <div className="chat-start-zk relative">

+ 1 - 2
src/components/bs-comp/chatComponent/MessagePanne.tsx

@@ -10,7 +10,7 @@ import RunLog from "./RunLog";
 import Separator from "./Separator";
 import { useMessageStore } from "./messageStore";
 
-export default function MessagePanne({ useName, guideWord, loadMore, flow_type }) {
+export default function MessagePanne({logo, useName, guideWord, loadMore, flow_type }) {
     const { t } = useTranslation()
     const { chatId, messages } = useMessageStore()
 
@@ -75,7 +75,6 @@ export default function MessagePanne({ useName, guideWord, loadMore, flow_type }
                 } else if (msg.thought) {
                     type = 'system'
                 }
-                // console.log(type)
                 switch (type) {
                     case 'user':
                         return <MessageUser key={msg.id} useName={useName} data={msg} />;

+ 15 - 2
src/components/bs-comp/chatComponent/index.tsx

@@ -1,9 +1,22 @@
 import ChatInput from "./ChatInput";
 import MessagePanne from "./MessagePanne";
 
-export default function ChatComponent({ clear = false, questions = [], form = false, useName, inputForm = null, guideWord, wsUrl, onBeforSend, type, loadMore = () => { } }) {
+export default function ChatComponent({
+    stop = false,
+    logo = '',
+    clear = false,
+    questions = [],
+    form = false,
+    useName,
+    inputForm = null,
+    guideWord,
+    wsUrl,
+    onBeforSend,
+    type,
+    loadMore = () => { } 
+}) {
     return <div className="relative h-full">
-        <MessagePanne useName={useName} guideWord={guideWord} loadMore={loadMore} flow_type={type}></MessagePanne>
+        <MessagePanne logo={logo} useName={useName} guideWord={guideWord} loadMore={loadMore} flow_type={type}></MessagePanne>
         <ChatInput clear={clear} questions={questions} form={form} wsUrl={wsUrl} inputForm={inputForm} onBeforSend={onBeforSend} ></ChatInput>
     </div>
 };

+ 48 - 13
src/components/bs-comp/chatComponent/messageStore.ts

@@ -4,6 +4,7 @@ import { MessageDB, getChatHistory } from '@/controllers/API'
 import { ChatMessageType } from '@/types/chat'
 import { cloneDeep } from 'lodash'
 import { create } from 'zustand'
+import { formatDate } from '@/util/utils';
 
 /**
  * 会话消息管理
@@ -19,6 +20,8 @@ type State = {
     /** 没有更多历史纪录 */
     historyEnd: boolean,
     messages: ChatMessageType[]
+    /** 历史回话独立存储 */
+    hisMessages: ChatMessageType[]
     /**
      * 控制引导问题的显示状态
      */
@@ -26,8 +29,8 @@ type State = {
 }
 
 type Actions = {
-    loadHistoryMsg: (flowid: string, chatId: string, flow_type: string) => Promise<void>;
-    loadMoreHistoryMsg: (flowid: string, flow_type: string) => Promise<void>;
+    loadHistoryMsg: (flowid: string, chatId: string, data: { appendHistory: boolean, lastMsg: string }, flow_type: string) => Promise<void>;
+    loadMoreHistoryMsg: (flowid: string, appendHistory: boolean, flow_type: string) => Promise<void>;
     destory: () => void;
     createSendMsg: (inputs: any, inputKey?: string) => void;
     createWsMsg: (data: any) => void;
@@ -38,6 +41,7 @@ type Actions = {
     insetSystemMsg: (text: string) => void;
     insetBsMsg: (text: string) => void;
     setShowGuideQuestion: (text: boolean) => void;
+    clearMsgs: () => void;
 }
 
 
@@ -76,18 +80,32 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
     running: false,
     chatId: '',
     messages: [],
+    hisMessages: [],
     historyEnd: false,
     showGuideQuestion: false,
     setShowGuideQuestion(bln: boolean) {
         set({ showGuideQuestion: bln })
     },
-    async loadHistoryMsg(flowid, chatId, flow_type) {
+    async loadHistoryMsg(flowid, chatId, { appendHistory, lastMsg }, flow_type) {
         const res = await getChatHistory(flowid, chatId, 30, 0, flow_type)
         const msgs = handleHistoryMsg(res)
         currentChatId = chatId
-        set({ historyEnd: false, messages: msgs.reverse() })
+        const hisMessages = appendHistory ? [] : msgs.reverse()
+        if (hisMessages.length) {
+            hisMessages.push({
+                ...bsMsgItem,
+                id: Math.random() * 1000000,
+                category: 'divider',
+                message: lastMsg,
+            })
+        }
+        set({
+            historyEnd: false,
+            messages: appendHistory ? msgs.reverse() : [],
+            hisMessages
+        })
     },
-    async loadMoreHistoryMsg(flowid, flow_type) {
+    async loadMoreHistoryMsg(flowid, appendHistory, flow_type) {
         if (get().running) return // 会话进行中禁止加载more历史
         if (get().historyEnd) return // 没有更多历史纪录
         const chatId = get().chatId
@@ -101,11 +119,16 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
         }
         const msgs = handleHistoryMsg(res)
         if (msgs.length) {
-            set({ messages: [...msgs.reverse(), ...prevMsgs] })
+            set({ [appendHistory ? 'messages' : 'hisMessages']: [...msgs.reverse(), ...prevMsgs] })
         } else {
             set({ historyEnd: true })
         }
     },
+    clearMsgs() {
+        setTimeout(() => {
+            set({ hisMessages: [], messages: [], historyEnd: true })
+        }, 0);
+    },
     destory() {
         set({ chatId: '', messages: [] })
     },
@@ -122,7 +145,8 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
                     category: '',
                     files: [],
                     end: false,
-                    user_name: ""
+                    user_name: "",
+                    update_time: formatDate(new Date(), 'yyyy-MM-ddTHH:mm:ss')
                 }]
         }))
     },
@@ -151,13 +175,20 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
         // if (wsdata.end) {
         //     debugger
         // }
-        console.log('change updateCurrentMessage');
+        // console.log('change updateCurrentMessage');
         const messages = get().messages
         const isRunLog = runLogsTypes.includes(wsdata.category);
         // run log类型存在嵌套情况,使用 extra 匹配 currentMessage; 否则取最近
-        const currentMessageIndex = isRunLog ?
-            messages.findLastIndex((msg) => msg.extra === wsdata.extra)
-            : messages.findLastIndex((msg) => !runLogsTypes.includes(msg.category))
+        let currentMessageIndex = 0
+        for (let i = messages.length - 1; i >= 0; i--) {
+            if (isRunLog && messages[i].extra === wsdata.extra) {
+                currentMessageIndex = i;
+                break;
+            } else if (!isRunLog && !runLogsTypes.includes(messages[i].category)) {
+                currentMessageIndex = i;
+                break;
+            }
+        }
         const currentMessage = messages[currentMessageIndex]
 
         const newCurrentMessage = {
@@ -166,13 +197,17 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
             id: isRunLog ? wsdata.extra : wsdata.messageId, // 每条消息必唯一
             message: isRunLog ? JSON.parse(wsdata.message) : currentMessage.message + wsdata.message,
             thought: currentMessage.thought + (wsdata.thought ? `${wsdata.thought}\n` : ''),
-            files: wsdata.files || null,
+            files: wsdata.files || [],
             category: wsdata.category || '',
             source: wsdata.source
         }
+        // 无id补上(如文件解析完成消息,后端无返回messageid)
+        if (!newCurrentMessage.id) {
+            newCurrentMessage.id = Math.random() * 1000000
+            // console.log('msg:', newCurrentMessage);
+        }
 
         messages[currentMessageIndex] = newCurrentMessage
-        // console.log(messages,currentMessageIndex,newCurrentMessage)
         // 会话特殊处理,兼容后端的缺陷
         if (!isRunLog) {
             // start - end 之间没有内容删除load

+ 24 - 0
src/components/bs-comp/loadMore/index.tsx

@@ -0,0 +1,24 @@
+import { useEffect, useRef } from "react";
+
+export default function LoadMore({ onScrollLoad }) {
+    // scroll load
+    const footerRef = useRef<HTMLDivElement>(null)
+    useEffect(function () {
+        const observer = new IntersectionObserver((entries) => {
+            entries.forEach(entry => {
+                if (entry.isIntersecting) {
+                    onScrollLoad()
+                }
+            });
+        }, {
+            // root: null, // 视口
+            rootMargin: '0px', // 视口的边距
+            threshold: 0.1 // 目标元素超过视口的10%即触发回调
+        });
+
+        observer.observe(footerRef.current);
+        return () => footerRef.current && observer.unobserve(footerRef.current);
+    }, [])
+
+    return <div ref={footerRef} style={{ height: 20 }}></div>
+};

+ 48 - 0
src/components/bs-comp/selectComponent/Users.tsx

@@ -0,0 +1,48 @@
+import MultiSelect from "@/components/bs-ui/select/multi";
+import { getUsersApi } from "@/controllers/API/user";
+import { useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+
+export default function UsersSelect({ multiple = false, lockedValues = [], value, disabled = false, onChange, children }:
+    { multiple?: boolean, lockedValues?: any[], value: any, disabled?: boolean, onChange: (a: any) => any, children?: (fun: any) => React.ReactNode }) {
+
+    const { t } = useTranslation()
+    const [options, setOptions] = useState<any>([]);
+    const originOptionsRef = useRef([])
+
+    const pageRef = useRef(1)
+    const reload = (page, name) => {
+        getUsersApi({ page, pageSize: 40, name }).then(res => {
+            pageRef.current = page
+            originOptionsRef.current = res.data
+            const opts = res.data.map(el => ({ label: el.user_name, value: el.user_id }))
+            setOptions(_ops => page > 1 ? [..._ops, ...opts] : opts)
+        })
+    }
+
+    useEffect(() => {
+        reload(1, '')
+    }, [])
+
+    // 加载更多
+    const loadMore = (name) => {
+        reload(pageRef.current + 1, name)
+    }
+
+    return <MultiSelect
+        className=" max-w-[630px]"
+        multiple={multiple}
+        value={value}
+        lockedValues={lockedValues}
+        disabled={disabled}
+        options={options}
+        placeholder={'请选择用户'}
+        searchPlaceholder={'搜索用户名称'}
+        onChange={onChange}
+        onLoad={() => reload(1, '')}
+        onSearch={(val) => reload(1, val)}
+        onScrollLoad={(val) => loadMore(val)}
+    >
+        {children?.(reload)}
+    </MultiSelect>
+};

+ 51 - 0
src/components/bs-comp/selectComponent/knowledge.tsx

@@ -0,0 +1,51 @@
+import MultiSelect from "@/components/bs-ui/select/multi";
+import { readFileLibDatabase } from "@/controllers/API";
+import { useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+
+export default function KnowledgeSelect({ multiple = false, value, disabled = false, onChange, children }:
+    { multiple?: boolean, value: any, disabled?: boolean, onChange: (a: any) => any, children?: (fun: any) => React.ReactNode }) {
+
+    const { t } = useTranslation()
+    const [options, setOptions] = useState<any>([]);
+    const originOptionsRef = useRef([])
+
+    const pageRef = useRef(1)
+    const reload = (page, name) => {
+        readFileLibDatabase(page, 60, name).then(res => {
+            pageRef.current = page
+            originOptionsRef.current = res.data
+            const opts = res.data.map(el => ({ label: el.name, value: el.id }))
+            setOptions(_ops => page > 1 ? [..._ops, ...opts] : opts)
+        })
+    }
+
+    useEffect(() => {
+        reload(1, '')
+    }, [])
+
+    // const handleChange = (res) => {
+    //     // id => obj
+    //     onChange(res.map(el => originOptionsRef.current.find(el2 => el2.id === el)))
+    // }
+
+    // 加载更多
+    const loadMore = (name) => {
+        reload(pageRef.current + 1, name)
+    }
+
+    return <MultiSelect
+        multiple={multiple}
+        value={value}
+        disabled={disabled}
+        options={options}
+        placeholder={t('build.selectKnowledgeBase')}
+        searchPlaceholder={t('build.searchBaseName')}
+        onChange={onChange}
+        onLoad={() => reload(1, '')}
+        onSearch={(val) => reload(1, val)}
+        onScrollLoad={(val) => loadMore(val)}
+    >
+        {children?.(reload)}
+    </MultiSelect>
+};

+ 38 - 49
src/components/bs-comp/sheets/SkillChatSheet.tsx

@@ -19,6 +19,8 @@ import zidingyi1 from "../../../assets/npc/zidingyi1.png";
 import zidingyi2 from "../../../assets/npc/zidingyi2.png";
 import npcIcon from "../../../assets/npc/npcIcon.png";
 import nengliIcon from "../../../assets/npc/nengliIcon.png";
+import { useDebounce } from "@/util/hook";
+import LoadMore from "../loadMore";
 
 export default function SkillChatSheet({ children, onSelect }) {
     const [open, setOpen] = useState(false)
@@ -30,34 +32,45 @@ export default function SkillChatSheet({ children, onSelect }) {
     const [keyword, setKeyword] = useState(' ')
     const allDataRef = useRef([])
 
-    useEffect(() => {
-        open && getChatOnlineApi().then(res => {
-            allDataRef.current = res
-            setKeyword('')
+    const pageRef = useRef(1)
+    const searchRef = useRef('')
+    const [options, setOptions] = useState<any>([])
+
+    const loadData = (more = false) => {
+        open && getChatOnlineApi(pageRef.current, searchRef.current).then(res => {
+            setOptions(opts => more ? [...opts, ...res] : res)
         })
+    }
+    const debounceLoad = useDebounce(loadData, 600, false)
+
+    useEffect(() => {
+        // open && getChatOnlineApi().then(res => {
+        //     allDataRef.current = res
+        //     setKeyword('')
+        // })
         // setKeyword(' ')
+        pageRef.current = 1
+        searchRef.current = ''
+        loadData()
     }, [open])
 
-    const options = useMemo(() => {
-        return allDataRef.current.filter(el => el.name.toLowerCase().includes(keyword.toLowerCase()))
-    }, [keyword])
+    // const options = useMemo(() => {
+    //     return allDataRef.current.filter(el => el.name.toLowerCase().includes(keyword.toLowerCase()))
+    // }, [keyword])
+
+    const handleSearch = (e) => {
+        pageRef.current = 1
+        searchRef.current = e.target.value
+        debounceLoad()
+    }
+
+    const handleLoadMore = () => {
+        pageRef.current++
+        loadData(true)
+    }
 
     const render = (item: any) => (
         <Flexbox align={'flex-start'} className={`selectNpcFlexbox relative`} onClick={() => { onSelect(item); setOpen(false) }}>
-          {/* <Avatar size={24} src={item.favicon} style={{ flex: 'none' }} /> */}
-          {/* <Flexbox>
-            <div style={{ fontSize: 15, fontWeight: 600 }}>{item.name}</div>
-            <div style={{ opacity: 0.6 }}>{item.name}</div>
-          </Flexbox> */}
-            {/* <Card key={item.id} className="w-[300px]  overflow-hidden cursor-pointer" onClick={() => onSelect(item)}>
-                <CardHeader>
-                    <CardTitle className=" flex items-center gap-2">
-                        <div className={"rounded-full w-[30px] h-[30px] " + gradients[parseInt(item.id, 16) % gradients.length]}></div>
-                        <span>{item.name}</span>
-                    </CardTitle>
-                    <CardDescription className="">{item.description}</CardDescription>        
-                </CardHeader>
-            </Card> */}
             <div className="npcInfoItemBg">
                 <span>
                     <span>
@@ -68,16 +81,11 @@ export default function SkillChatSheet({ children, onSelect }) {
                 </span>
             </div>
             <div>
-                {/* {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[42px]" alt=""/>}
-                {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[42px]" alt=""/>}
-                {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[42px]" alt=""/>} */}
-                {/* <img src={robot} className="w-[42px]" alt=""/> */}
                 <TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : (item.flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>
                 <div>
                     <p>{item.name}</p>
                     <div>
-                        {/* <div>绘画类</div>
-                        <div>绘画类</div> */}
+                     
                     </div>
                 </div>
             </div>
@@ -100,31 +108,12 @@ export default function SkillChatSheet({ children, onSelect }) {
                 <div className="w-[280px] p-6">
                     <SheetTitle>选择对话</SheetTitle>
                     <SheetDescription className="text-[#999999]">选择一个您想使用的上线NPC或能力</SheetDescription>
-                    <SearchInput value={keyword} placeholder="搜索" className="my-6" onChange={(e) => setKeyword(e.target.value)} />
+                    {/* <SearchInput value={keyword} placeholder="搜索" className="my-6" onChange={(e) => setKeyword(e.target.value)} /> */}
+                    <SearchInput placeholder="搜索" className="my-6" onChange={handleSearch} />
                 </div>
                 <div className="w-[690px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
-                    {/* {
-                        options.length ? options.map((flow, i) => (
-                            <CardComponent key={i}
-                                id={i + 1}
-                                data={flow}
-                                title={flow.name}
-                                description={flow.desc}
-                                type="sheet"
-                                icon={flow.flow_type === 'flow' ? SkillIcon : AssistantIcon}
-                                footer={
-                                    <Badge className={`absolute right-0 bottom-0 rounded-none rounded-br-md ${flow.flow_type === 'flow' && 'bg-gray-950'}`}>
-                                        {flow.flow_type === 'flow' ? '技能' : 'NPC'}
-                                    </Badge>
-                                }
-                                onClick={() => { onSelect(flow); setOpen(false) }}
-                            />
-                        )) : <div className="flex flex-col items-center justify-center pt-40 w-full">
-                            <p className="text-sm text-muted-foreground mb-3">{t('build.empty')}</p>
-                            <Button className="w-[200px]" onClick={() => navigate('/build/assist')}>{t('build.onlineSA')}</Button>
-                        </div>
-                    } */}
                     <SpotlightCard items={options} renderItem={render} className="mt-[14px] skillSheetSpotlightCard"/>
+                    <LoadMore onScrollLoad={handleLoadMore} />
                 </div>
             </div>
         </SheetContent>

BIN
src/components/bs-icons/.DS_Store


+ 3 - 3
src/components/bs-icons/del/Del.svg

@@ -1,11 +1,11 @@
 <svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
     <g id="Group 468">
         <g id="Group 466">
-            <rect id="Rectangle 221" x="8" y="9" width="12" height="1.71429" rx="0.857143" fill="#9CA3BA" />
-            <rect id="Rectangle 222" x="11.4285" y="7" width="5.14286" height="1.71429" rx="0.857143" fill="#9CA3BA" />
+            <rect id="Rectangle 221" x="8" y="9" width="12" height="1.71429" rx="0.857143" fill="#666" />
+            <rect id="Rectangle 222" x="11.4285" y="7" width="5.14286" height="1.71429" rx="0.857143" fill="#666" />
             <path id="Subtract" fill-rule="evenodd" clip-rule="evenodd"
                 d="M9 12C9 11.4477 9.44772 11 10 11H18C18.5523 11 19 11.4477 19 12V19C19 20.1046 18.1046 21 17 21H11C9.89543 21 9 20.1046 9 19V12ZM12.7143 14C13.1877 14 13.5714 14.3838 13.5714 14.8571V17.1429C13.5714 17.6162 13.1877 18 12.7143 18C12.2409 18 11.8572 17.6162 11.8572 17.1429V14.8571C11.8572 14.3838 12.2409 14 12.7143 14ZM16.1429 14.8571C16.1429 14.3838 15.7591 14 15.2857 14C14.8123 14 14.4286 14.3838 14.4286 14.8571V17.1429C14.4286 17.6162 14.8123 18 15.2857 18C15.7591 18 16.1429 17.6162 16.1429 17.1429V14.8571Z"
-                fill="#9CA3BA" />
+                fill="#666" />
         </g>
     </g>
 </svg>

+ 1 - 1
src/components/bs-icons/del/index.tsx

@@ -5,5 +5,5 @@ export const DelIcon = forwardRef<
     SVGSVGElement & { className: any },
     React.PropsWithChildren<{ className?: string }>
 >((props, ref) => {
-    return <Del ref={ref} {...props} />;
+    return <Del className="text-[red]" ref={ref} {...props} />;
 });

+ 7 - 0
src/components/bs-icons/down/DropDown.svg

@@ -0,0 +1,7 @@
+<svg t="1718350872747" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2516"
+    width="20" height="20">
+    <path
+        d="M511.36 712.32L240.853333 441.813333l30.293334-30.293333 241.493333 241.493333 262.186667-241.92 29.013333 31.146667-292.48 270.08z"
+        p-id="2517">
+    </path>
+</svg>

+ 9 - 0
src/components/bs-icons/down/index.tsx

@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as DropDown } from "./DropDown.svg";
+
+export const DropDownIcon = forwardRef<
+    SVGSVGElement & { className: any },
+    React.PropsWithChildren<{ className?: string }>
+>((props, ref) => {
+    return <DropDown ref={ref} {...props} />;
+});

+ 1 - 1
src/components/bs-icons/en/En.svg

@@ -1,3 +1,3 @@
 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M4.61375 12.536H9.09375V15H2.05375V3.8H9.01375V6.264H4.61375V8.12H8.61375V10.552H4.61375V12.536ZM15.1461 6.776C16.7621 6.776 18.1381 7.928 18.1381 10.088V15H15.7381V10.44C15.7381 9.448 15.1141 8.984 14.3461 8.984C13.4661 8.984 12.8581 9.496 12.8581 10.632V15H10.4581V7H12.8581V7.752C13.2901 7.16 14.0901 6.776 15.1461 6.776Z" fill="black"/>
+<path d="M4.61375 12.536H9.09375V15H2.05375V3.8H9.01375V6.264H4.61375V8.12H8.61375V10.552H4.61375V12.536ZM15.1461 6.776C16.7621 6.776 18.1381 7.928 18.1381 10.088V15H15.7381V10.44C15.7381 9.448 15.1141 8.984 14.3461 8.984C13.4661 8.984 12.8581 9.496 12.8581 10.632V15H10.4581V7H12.8581V7.752C13.2901 7.16 14.0901 6.776 15.1461 6.776Z" fill="currentColor"/>
 </svg>

+ 6 - 0
src/components/bs-icons/filter/Filter.svg

@@ -0,0 +1,6 @@
+<svg t="1716545040041" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" 
+p-id="2664" width="18" height="18">
+<path d="M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196z m531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z" 
+p-id="2665" fill="currentColor">
+</path>
+</svg>

+ 9 - 0
src/components/bs-icons/filter/index.tsx

@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as Filter } from "./Filter.svg";
+
+export const FilterIcon = forwardRef<
+    SVGSVGElement & { className: any },
+    React.PropsWithChildren<{ className?: string }>
+>((props, ref) => {
+    return <Filter ref={ref} {...props} />;
+});

+ 40 - 0
src/components/bs-icons/index.ts

@@ -0,0 +1,40 @@
+export { AddToIcon } from './addTo';
+export { ApplicationIcon } from './menu/application';
+export { AssistantIcon } from './assistant';
+export { AvatarIcon } from './avatar';
+export { BookOpenIcon } from './bookOpen';
+export { ClearIcon } from './clear';
+export { DelIcon } from './del';
+export { EnIcon } from './en';
+export { FilterIcon } from './filter';
+export { FormIcon } from './form';
+export { GithubIcon } from './github';
+export { GoIcon } from './go';
+export { KnowledgeIcon } from './knowledge';
+export { LoadIcon } from './loading';
+export { ModelIcon } from './menu/model';
+export { MoonIcon } from './moon';
+export { MoveOneIcon } from './moveOne';
+export { NewApplicationIcon } from './newApplication';
+export { WordIcon } from './office';
+export { PlusIcon } from './plus';
+export { PlusBoxIcon } from './plusBox';
+export { QuestionMarkIcon } from './questionMark';
+export { QuitIcon } from './quit';
+export { SaveIcon } from './save';
+export { SearchIcon } from './search';
+export { SendIcon } from './send';
+export { SettingIcon } from './setting';
+export { SkillIcon } from './skill';
+export { SystemIcon } from './menu/system';
+export { TabIcon } from './tab';
+export { TechnologyIcon } from './menu/technology';
+export { ThunmbIcon } from './thumbs';
+export { TipIcon } from './tip';
+export { ToastIcon } from './toast';
+export { ToolIcon } from './tool';
+export { UploadIcon } from './upload';
+export { UserIcon } from './user';
+export { LogIcon } from './menu/log';
+export { EvaluatingIcon } from './menu/evaluation';
+export { DropDownIcon } from './down'

+ 0 - 0
src/components/bs-icons/application/Application.svg → src/components/bs-icons/menu/application/Application.svg


+ 0 - 0
src/components/bs-icons/application/index.tsx → src/components/bs-icons/menu/application/index.tsx


+ 6 - 0
src/components/bs-icons/menu/evaluation/Evaluation.svg

@@ -0,0 +1,6 @@
+<svg t="1719471279077" class="icon" viewBox="-160 -160 1344 1344" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24"
+    height="24">
+    <path
+        d="M887.994182 46.545455H146.245818C95.976727 46.545455 46.545455 88.110545 46.545455 138.612364V512h183.761454L298.821818 446.370909c7.68-7.68 18.618182-9.448727 29.230546-7.447273a33.512727 33.512727 0 0 1 23.738181 20.014546l83.409455 196.096 129.117091-389.259637a33.186909 33.186909 0 0 1 30.72-22.202181c15.266909 0.325818 27.508364 6.656 31.464727 20.573091L697.809455 512h92.625454a31.650909 31.650909 0 1 1 0 63.488h-117.201454c-14.522182 0-27.275636-5.585455-31.278546-19.549091l-49.198545-171.054545-118.784 371.712c-4.282182 12.8-25.460364 22.248727-25.460364 22.807272v0.279273c-21.131636 0-30.068364-7.586909-35.281455-19.549091l-100.258909-230.4-45.102545 40.308364c-6.050909 6.097455-15.406545 5.445818-24.064 5.445818H46.545455v309.248C46.545455 935.284364 96.023273 977.454545 146.245818 977.454545h741.748364c50.269091 0 89.460364-42.170182 89.460363-92.718545V138.612364C977.454545 88.110545 938.216727 46.545455 887.994182 46.545455z"
+        fill="currentColor"></path>
+</svg>

+ 9 - 0
src/components/bs-icons/menu/evaluation/index.tsx

@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as Icon } from "./Evaluation.svg";
+
+export const EvaluatingIcon = forwardRef<
+    SVGSVGElement & { className: any },
+    React.PropsWithChildren<{ className?: string }>
+>(({ className, ...props }, ref) => {
+    return <Icon ref={ref} {...props} className={className || ''} />;
+});

+ 6 - 0
src/components/bs-icons/menu/log/Log.svg

@@ -0,0 +1,6 @@
+<svg t="1717680812187" class="icon" viewBox="-100 -100 1224 1224" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7890"
+    width="24" height="24">
+    <path
+        d="M511.999269 0c4.608 0 9.289143 1.755429 13.165714 5.046857 42.422857 36.644571 185.051429 144.237714 405.650286 150.454857 11.117714 0.438857 20.187429 9.508571 20.041142 21.065143V605.622857C950.856411 797.622857 668.232411 1024 511.999269 1024c-160.621714 0-438.857143-226.377143-438.857143-418.377143V176.347429c0-11.483429 9.069714-20.626286 20.187428-20.918858C314.001554 149.284571 456.703269 41.691429 498.833554 4.900571A20.626286 20.626286 0 0 1 511.999269 0z m-24.356572 283.209143l-130.194286 270.336h204.214858L487.496411 712.704l75.044572 34.962286 129.024-277.065143H489.324983l72.996571-151.552-74.678857-35.84z"
+        fill="currentColor"></path>
+</svg>

+ 9 - 0
src/components/bs-icons/menu/log/index.tsx

@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import { ReactComponent as Log } from "./Log.svg";
+
+export const LogIcon = forwardRef<
+    SVGSVGElement & { className: any },
+    React.PropsWithChildren<{ className?: string }>
+>(({ className, ...props }, ref) => {
+    return <Log ref={ref} {...props} className={className || ''} />;
+});

+ 0 - 0
src/components/bs-icons/model/Model.svg → src/components/bs-icons/menu/model/Model.svg


+ 0 - 0
src/components/bs-icons/model/index.tsx → src/components/bs-icons/menu/model/index.tsx


+ 0 - 0
src/components/bs-icons/system/System.svg → src/components/bs-icons/menu/system/System.svg


+ 0 - 0
src/components/bs-icons/system/index.tsx → src/components/bs-icons/menu/system/index.tsx


+ 0 - 0
src/components/bs-icons/technology/Technology.svg → src/components/bs-icons/menu/technology/Technology.svg


+ 0 - 0
src/components/bs-icons/technology/index.tsx → src/components/bs-icons/menu/technology/index.tsx


+ 1 - 1
src/components/bs-icons/office/index.tsx

@@ -5,6 +5,6 @@ export const WordIcon = forwardRef<
     SVGSVGElement & { className: any },
     React.PropsWithChildren<{ className?: string }>
 >(({ className, ...props }, ref) => {
-    const _className = 'transition text-[#43AFD2] ' + (className || '')
+    const _className = 'transition text-gray-950 ' + (className || '')
     return <Word ref={ref} {...props} className={_className} />;
 });

+ 12 - 9
src/components/bs-icons/plusBox/PlusBox.svg

@@ -1,10 +1,13 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 541">
-<path id="Rectangle 247" d="M0 4C0 3.44772 0.447715 3 1 3H18C21.3137 3 24 5.68629 24 9V15C24 18.3137 21.3137 21 18 21H6C2.68629 21 0 18.3137 0 15V4Z" fill="#111111"/>
-<g id="Group 540">
-<g id="Group 414">
-<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M13 11V8H11L11 11H8V13H11L11 16H13V13H16V11H13Z" fill="white"/>
-</g>
-</g>
-</g>
-</svg>
+    <g id="Group 541">
+        <path id="Rectangle 247"
+            d="M0 4C0 3.44772 0.447715 3 1 3H18C21.3137 3 24 5.68629 24 9V15C24 18.3137 21.3137 21 18 21H6C2.68629 21 0 18.3137 0 15V4Z"
+            fill="#111111" />
+        <g id="Group 540">
+            <g id="Group 414">
+                <path id="Union" fill-rule="evenodd" clip-rule="evenodd"
+                    d="M13 11V8H11L11 11H8V13H11L11 16H13V13H16V11H13Z" fill="white" />
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 1
src/components/bs-icons/questionMark/index.tsx

@@ -5,6 +5,6 @@ export const QuestionMarkIcon = forwardRef<
     SVGSVGElement & { className: any },
     React.PropsWithChildren<{ className?: string }>
 >(({ className, ...props }, ref) => {
-    const _className = 'transition text-gray-950 ' + (className || '')
+    const _className = 'transition text-[#999] ' + (className || '')
     return <QuestionMark ref={ref} {...props} className={_className} />;
 });

+ 0 - 8
src/components/bs-icons/quit/Quit-dark.svg

@@ -1,8 +0,0 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 542">
-<g id="Group 417">
-<path id="Rectangle 186" d="M10 5H7.2C6.0799 5 5.51984 5 5.09202 5.21799C4.71569 5.40973 4.40973 5.71569 4.21799 6.09202C4 6.51984 4 7.07989 4 8.2V15.8C4 16.9201 4 17.4802 4.21799 17.908C4.40973 18.2843 4.71569 18.5903 5.09202 18.782C5.51984 19 6.07989 19 7.2 19H10" stroke="white" stroke-width="2"/>
-<path id="Vector 1" d="M10 12H17.5M15 8L19 12L15 16" stroke="white" stroke-width="2" stroke-linejoin="round"/>
-</g>
-</g>
-</svg>

+ 10 - 7
src/components/bs-icons/quit/Quit.svg

@@ -1,8 +1,11 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 542">
-<g id="Group 417">
-<path id="Rectangle 186" d="M10 5H7.2C6.0799 5 5.51984 5 5.09202 5.21799C4.71569 5.40973 4.40973 5.71569 4.21799 6.09202C4 6.51984 4 7.07989 4 8.2V15.8C4 16.9201 4 17.4802 4.21799 17.908C4.40973 18.2843 4.71569 18.5903 5.09202 18.782C5.51984 19 6.07989 19 7.2 19H10" stroke="black" stroke-width="2"/>
-<path id="Vector 1" d="M10 12H17.5M15 8L19 12L15 16" stroke="black" stroke-width="2" stroke-linejoin="round"/>
-</g>
-</g>
-</svg>
+    <g id="Group 542">
+        <g id="Group 417">
+            <path id="Rectangle 186"
+                d="M10 5H7.2C6.0799 5 5.51984 5 5.09202 5.21799C4.71569 5.40973 4.40973 5.71569 4.21799 6.09202C4 6.51984 4 7.07989 4 8.2V15.8C4 16.9201 4 17.4802 4.21799 17.908C4.40973 18.2843 4.71569 18.5903 5.09202 18.782C5.51984 19 6.07989 19 7.2 19H10"
+                stroke="currentColor" stroke-width="2" />
+            <path id="Vector 1" d="M10 12H17.5M15 8L19 12L15 16" stroke="currentColor" stroke-width="2"
+                stroke-linejoin="round" />
+        </g>
+    </g>
+</svg>

+ 2 - 10
src/components/bs-icons/quit/index.tsx

@@ -1,17 +1,9 @@
 import React, { forwardRef } from "react";
 import { ReactComponent as Quit } from "./Quit.svg";
-import { ReactComponent as QuitDark } from "./Quit-dark.svg";
 
 export const QuitIcon = forwardRef<
     SVGSVGElement & { className: any },
     React.PropsWithChildren<{ className?: string }>
->(({className,...props}, ref) => {
-    return <Quit ref={ref} {...props} className={className || ''}/>;
-});
-
-export const QuitIconDark = forwardRef<
-    SVGSVGElement & { className: any },
-    React.PropsWithChildren<{ className?: string }>
->(({className,...props}, ref) => {
-    return <QuitDark ref={ref} {...props} className={className || ''}/>;
+>(({ className, ...props }, ref) => {
+    return <Quit ref={ref} {...props} className={className || ''} />;
 });

+ 2 - 2
src/components/bs-icons/search/Search.svg

@@ -1,8 +1,8 @@
 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
     <g id="Group 459">
         <g id="Group 458">
-            <circle id="Ellipse 10" cx="9" cy="9" r="6" stroke="black" stroke-width="2" />
-            <path id="Vector 4" d="M13 13L17 17" stroke="black" stroke-width="2" stroke-linejoin="round" />
+            <circle id="Ellipse 10" cx="9" cy="9" r="6" stroke="currentColor" stroke-width="2" />
+            <path id="Vector 4" d="M13 13L17 17" stroke="currentColor" stroke-width="2" stroke-linejoin="round" />
         </g>
     </g>
 </svg>

+ 3 - 3
src/components/bs-icons/thumbs/copy.svg

@@ -1,8 +1,8 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
     <g id="Group 488">
-        <rect id="Rectangle 231" x="5.75" y="5.75" width="12.5" height="13.5" rx="2.25" fill="white"
+        <rect id="Rectangle 231" x="5.75" y="5.75" width="12.5" height="13.5" rx="2.25" fill="transparent"
+            stroke="currentColor" stroke-width="1.5" />
+        <rect id="Rectangle 232" x="8.75" y="4.75" width="6.5" height="3.5" rx="1.25" fill="transparent"
             stroke="currentColor" stroke-width="1.5" />
-        <rect id="Rectangle 232" x="8.75" y="4.75" width="6.5" height="3.5" rx="1.25" fill="white" stroke="currentColor"
-            stroke-width="1.5" />
     </g>
 </svg>

+ 0 - 6
src/components/bs-icons/thumbs/copyDark.svg

@@ -1,6 +0,0 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 488">
-<rect id="Rectangle 231" x="5.75" y="5.75" width="12.5" height="13.5" rx="2.25" fill="#1B1B1B" stroke="#898C97" stroke-width="1.5"/>
-<rect id="Rectangle 232" x="8.75" y="4.75" width="6.5" height="3.5" rx="1.25" fill="#1B1B1B" stroke="#898C97" stroke-width="1.5"/>
-</g>
-</svg>

+ 2 - 8
src/components/bs-icons/thumbs/index.tsx

@@ -1,11 +1,8 @@
 import React, { forwardRef } from "react";
 import { ReactComponent as copy } from "./copy.svg";
-import { ReactComponent as copyDark } from "./copyDark.svg";
 import { ReactComponent as like } from "./like.svg";
-import { ReactComponent as likeDark } from "./likeDark.svg";
 import { ReactComponent as unLike } from "./unLike.svg";
-import { ReactComponent as unLikeDark } from "./unLikeDark.svg";
-
+import { cname } from "@/components/bs-ui/utils";
 
 type ThunmbIconType = 'copy' | 'like' | 'unLike' | 'copyDark' | 'likeDark' | 'unLikeDark';
 
@@ -15,13 +12,10 @@ export const ThunmbIcon = forwardRef<
 >((props, ref) => {
     const comps = {
         'copy': copy,
-        'copyDark': copyDark,
         'like': like,
-        'likeDark': likeDark,
         'unLike': unLike,
-        'unLikeDark': unLikeDark,
     }
     const Comp = comps[props.type];
-    const _className = 'transition text-gray-400 ' + (props.className || '')
+    const _className = cname('transition text-gray-400 hover:text-gray-500', props.className)
     return <Comp ref={ref} {...props} className={_className} />;
 });

+ 4 - 4
src/components/bs-icons/thumbs/like.svg

@@ -1,15 +1,15 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
     <g id="Group 489">
-        <rect id="Rectangle 231" x="4.75" y="9.75" width="3.5" height="8.5" rx="1.75" fill="white" stroke="currentColor"
-            stroke-width="1.5" />
+        <rect id="Rectangle 231" x="4.75" y="9.75" width="3.5" height="8.5" rx="1.75" fill="transparent"
+            stroke="currentColor" stroke-width="1.5" />
         <g id="Union">
-            <mask id="path-2-inside-1_399_44" fill="white">
+            <mask id="path-2-inside-1_399_44" fill="#fff">
                 <path fill-rule="evenodd" clip-rule="evenodd"
                     d="M13.2231 4C12.1561 4 11.1694 4.56673 10.6318 5.48839L8.89567 8.46456C8.05682 8.99647 7.5 9.93321 7.5 11V16C7.5 17.6569 8.84315 19 10.5 19H14.6983C16.3803 19 17.8826 17.9477 18.4575 16.367L20.2802 11.3544C20.8731 9.7239 19.6657 8 17.9307 8H14.5V5C14.5 4.44772 14.0523 4 13.5 4H13.2231Z" />
             </mask>
             <path fill-rule="evenodd" clip-rule="evenodd"
                 d="M13.2231 4C12.1561 4 11.1694 4.56673 10.6318 5.48839L8.89567 8.46456C8.05682 8.99647 7.5 9.93321 7.5 11V16C7.5 17.6569 8.84315 19 10.5 19H14.6983C16.3803 19 17.8826 17.9477 18.4575 16.367L20.2802 11.3544C20.8731 9.7239 19.6657 8 17.9307 8H14.5V5C14.5 4.44772 14.0523 4 13.5 4H13.2231Z"
-                fill="white" />
+                fill="transparent" />
             <path
                 d="M10.6318 5.48839L9.33611 4.73258V4.73258L10.6318 5.48839ZM8.89567 8.46456L9.69893 9.73136L10.0073 9.5358L10.1913 9.22037L8.89567 8.46456ZM18.4575 16.367L19.8672 16.8796V16.8796L18.4575 16.367ZM20.2802 11.3544L21.6899 11.867L20.2802 11.3544ZM14.5 8H13V9.5H14.5V8ZM11.9274 6.24419C12.1963 5.78337 12.6896 5.5 13.2231 5.5V2.5C11.6226 2.5 10.1426 3.3501 9.33611 4.73258L11.9274 6.24419ZM10.1913 9.22037L11.9274 6.24419L9.33611 4.73258L7.6 7.70876L10.1913 9.22037ZM9 11C9 10.4682 9.27563 9.99977 9.69893 9.73136L8.09241 7.19777C6.83801 7.99317 6 9.3982 6 11H9ZM9 16V11H6V16H9ZM10.5 17.5C9.67157 17.5 9 16.8284 9 16H6C6 18.4853 8.01472 20.5 10.5 20.5V17.5ZM14.6983 17.5H10.5V20.5H14.6983V17.5ZM17.0478 15.8544C16.6885 16.8423 15.7496 17.5 14.6983 17.5V20.5C17.0111 20.5 19.0768 19.0531 19.8672 16.8796L17.0478 15.8544ZM18.8705 10.8417L17.0478 15.8544L19.8672 16.8796L21.6899 11.867L18.8705 10.8417ZM17.9307 9.5C18.6247 9.5 19.1077 10.1896 18.8705 10.8417L21.6899 11.867C22.6386 9.25824 20.7066 6.5 17.9307 6.5V9.5ZM14.5 9.5H17.9307V6.5H14.5V9.5ZM13 5V8H16V5H13ZM13.5 5.5C13.2239 5.5 13 5.27614 13 5H16C16 3.61929 14.8807 2.5 13.5 2.5V5.5ZM13.2231 5.5H13.5V2.5H13.2231V5.5Z"
                 fill="currentColor" mask="url(#path-2-inside-1_399_44)" />

+ 0 - 12
src/components/bs-icons/thumbs/likeDark.svg

@@ -1,12 +0,0 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 489">
-<rect id="Rectangle 231" x="4.75" y="9.75" width="3.5" height="8.5" rx="1.75" fill="#1B1B1B" stroke="#898C97" stroke-width="1.5"/>
-<g id="Union">
-<mask id="path-2-inside-1_387_497" fill="white">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2231 4C12.1561 4 11.1694 4.56673 10.6318 5.48839L8.89567 8.46456C8.05682 8.99647 7.5 9.93321 7.5 11V16C7.5 17.6569 8.84315 19 10.5 19H14.6983C16.3803 19 17.8826 17.9477 18.4575 16.367L20.2802 11.3544C20.8731 9.7239 19.6657 8 17.9307 8H14.5V5C14.5 4.44772 14.0523 4 13.5 4H13.2231Z"/>
-</mask>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2231 4C12.1561 4 11.1694 4.56673 10.6318 5.48839L8.89567 8.46456C8.05682 8.99647 7.5 9.93321 7.5 11V16C7.5 17.6569 8.84315 19 10.5 19H14.6983C16.3803 19 17.8826 17.9477 18.4575 16.367L20.2802 11.3544C20.8731 9.7239 19.6657 8 17.9307 8H14.5V5C14.5 4.44772 14.0523 4 13.5 4H13.2231Z" fill="#1B1B1B"/>
-<path d="M10.6318 5.48839L9.33611 4.73258V4.73258L10.6318 5.48839ZM8.89567 8.46456L9.69893 9.73136L10.0073 9.5358L10.1913 9.22037L8.89567 8.46456ZM18.4575 16.367L19.8672 16.8796V16.8796L18.4575 16.367ZM20.2802 11.3544L21.6899 11.867L20.2802 11.3544ZM14.5 8H13V9.5H14.5V8ZM11.9274 6.24419C12.1963 5.78337 12.6896 5.5 13.2231 5.5V2.5C11.6226 2.5 10.1426 3.3501 9.33611 4.73258L11.9274 6.24419ZM10.1913 9.22037L11.9274 6.24419L9.33611 4.73258L7.6 7.70876L10.1913 9.22037ZM9 11C9 10.4682 9.27563 9.99977 9.69893 9.73136L8.09241 7.19777C6.83801 7.99317 6 9.3982 6 11H9ZM9 16V11H6V16H9ZM10.5 17.5C9.67157 17.5 9 16.8284 9 16H6C6 18.4853 8.01472 20.5 10.5 20.5V17.5ZM14.6983 17.5H10.5V20.5H14.6983V17.5ZM17.0478 15.8544C16.6885 16.8423 15.7496 17.5 14.6983 17.5V20.5C17.0111 20.5 19.0768 19.0531 19.8672 16.8796L17.0478 15.8544ZM18.8705 10.8417L17.0478 15.8544L19.8672 16.8796L21.6899 11.867L18.8705 10.8417ZM17.9307 9.5C18.6247 9.5 19.1077 10.1896 18.8705 10.8417L21.6899 11.867C22.6386 9.25824 20.7066 6.5 17.9307 6.5V9.5ZM14.5 9.5H17.9307V6.5H14.5V9.5ZM13 5V8H16V5H13ZM13.5 5.5C13.2239 5.5 13 5.27614 13 5H16C16 3.61929 14.8807 2.5 13.5 2.5V5.5ZM13.2231 5.5H13.5V2.5H13.2231V5.5Z" fill="#898C97" mask="url(#path-2-inside-1_387_497)"/>
-</g>
-</g>
-</svg>

+ 3 - 3
src/components/bs-icons/thumbs/unLike.svg

@@ -2,15 +2,15 @@
     <g id="Group 490">
         <g id="Group 491">
             <rect id="Rectangle 231" x="19.25" y="14.25" width="3.5" height="8.5" rx="1.75"
-                transform="rotate(-180 19.25 14.25)" fill="white" stroke="currentColor" stroke-width="1.5" />
+                transform="rotate(-180 19.25 14.25)" fill="transparent" stroke="currentColor" stroke-width="1.5" />
             <g id="Union">
-                <mask id="path-2-inside-1_399_50" fill="white">
+                <mask id="path-2-inside-1_399_50" fill="#fff">
                     <path fill-rule="evenodd" clip-rule="evenodd"
                         d="M10.0061 20C10.9504 20 11.8395 19.5554 12.4061 18.8L14.6872 15.7586C15.7568 15.3 16.5061 14.2375 16.5061 13L16.5061 8C16.5061 6.34315 15.163 5 13.5061 5L9.30781 5C7.62579 5 6.12346 6.05227 5.54864 7.63302L3.72587 12.6456C3.13298 14.2761 4.34044 16 6.07535 16L9.17277 16L8.70017 18.8356C8.59858 19.4451 9.06862 20 9.68656 20L10.0061 20Z" />
                 </mask>
                 <path fill-rule="evenodd" clip-rule="evenodd"
                     d="M10.0061 20C10.9504 20 11.8395 19.5554 12.4061 18.8L14.6872 15.7586C15.7568 15.3 16.5061 14.2375 16.5061 13L16.5061 8C16.5061 6.34315 15.163 5 13.5061 5L9.30781 5C7.62579 5 6.12346 6.05227 5.54864 7.63302L3.72587 12.6456C3.13298 14.2761 4.34044 16 6.07535 16L9.17277 16L8.70017 18.8356C8.59858 19.4451 9.06862 20 9.68656 20L10.0061 20Z"
-                    fill="white" />
+                    fill="transparent" />
                 <path
                     d="M12.4061 18.8L13.6061 19.7L13.6061 19.7L12.4061 18.8ZM14.6872 15.7586L14.0961 14.3799L13.7277 14.5379L13.4872 14.8586L14.6872 15.7586ZM5.54864 7.63302L4.13895 7.12041L4.13895 7.12041L5.54864 7.63302ZM3.72587 12.6456L2.31618 12.133L3.72587 12.6456ZM9.17277 16L10.6524 16.2466L10.9435 14.5L9.17277 14.5L9.17277 16ZM8.70017 18.8356L10.1798 19.0822L10.1798 19.0822L8.70017 18.8356ZM11.2061 17.9C10.9228 18.2777 10.4782 18.5 10.0061 18.5L10.0061 21.5C11.4225 21.5 12.7563 20.8331 13.6061 19.7L11.2061 17.9ZM13.4872 14.8586L11.2061 17.9L13.6061 19.7L15.8872 16.6586L13.4872 14.8586ZM15.0061 13C15.0061 13.6164 14.634 14.1493 14.0961 14.3799L15.2782 17.1372C16.8796 16.4507 18.0061 14.8586 18.0061 13L15.0061 13ZM15.0061 8L15.0061 13L18.0061 13L18.0061 8L15.0061 8ZM13.5061 6.5C14.3345 6.5 15.0061 7.17157 15.0061 8L18.0061 8C18.0061 5.51472 15.9914 3.5 13.5061 3.5L13.5061 6.5ZM9.30781 6.5L13.5061 6.5L13.5061 3.5L9.30781 3.5L9.30781 6.5ZM6.95833 8.14564C7.31759 7.15767 8.25655 6.5 9.30781 6.5L9.30781 3.5C6.99503 3.5 4.92933 4.94687 4.13895 7.12041L6.95833 8.14564ZM5.13556 13.1583L6.95833 8.14564L4.13895 7.12041L2.31618 12.133L5.13556 13.1583ZM6.07535 14.5C5.38139 14.5 4.8984 13.8104 5.13556 13.1583L2.31618 12.133C1.36755 14.7418 3.29949 17.5 6.07535 17.5L6.07535 14.5ZM9.17277 14.5L6.07535 14.5L6.07535 17.5L9.17277 17.5L9.17277 14.5ZM10.1798 19.0822L10.6524 16.2466L7.69318 15.7534L7.22058 18.589L10.1798 19.0822ZM9.68656 18.5C9.99553 18.5 10.2306 18.7774 10.1798 19.0822L7.22058 18.589C6.96661 20.1128 8.14171 21.5 9.68656 21.5L9.68656 18.5ZM10.0061 18.5L9.68656 18.5L9.68656 21.5L10.0061 21.5L10.0061 18.5Z"
                     fill="currentColor" mask="url(#path-2-inside-1_399_50)" />

+ 0 - 14
src/components/bs-icons/thumbs/unLikeDark.svg

@@ -1,14 +0,0 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="Group 490">
-<g id="Group 491">
-<rect id="Rectangle 231" x="19.25" y="14.25" width="3.5" height="8.5" rx="1.75" transform="rotate(-180 19.25 14.25)" fill="#1B1B1B" stroke="#898C97" stroke-width="1.5"/>
-<g id="Union">
-<mask id="path-2-inside-1_387_503" fill="white">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0061 20C10.9504 20 11.8395 19.5554 12.4061 18.8L14.6872 15.7586C15.7568 15.3 16.5061 14.2375 16.5061 13L16.5061 8C16.5061 6.34315 15.163 5 13.5061 5L9.30781 5C7.62579 5 6.12346 6.05227 5.54864 7.63302L3.72587 12.6456C3.13298 14.2761 4.34044 16 6.07535 16L9.17277 16L8.70017 18.8356C8.59858 19.4451 9.06862 20 9.68656 20L10.0061 20Z"/>
-</mask>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0061 20C10.9504 20 11.8395 19.5554 12.4061 18.8L14.6872 15.7586C15.7568 15.3 16.5061 14.2375 16.5061 13L16.5061 8C16.5061 6.34315 15.163 5 13.5061 5L9.30781 5C7.62579 5 6.12346 6.05227 5.54864 7.63302L3.72587 12.6456C3.13298 14.2761 4.34044 16 6.07535 16L9.17277 16L8.70017 18.8356C8.59858 19.4451 9.06862 20 9.68656 20L10.0061 20Z" fill="#1B1B1B"/>
-<path d="M12.4061 18.8L13.6061 19.7L13.6061 19.7L12.4061 18.8ZM14.6872 15.7586L14.0961 14.3799L13.7277 14.5379L13.4872 14.8586L14.6872 15.7586ZM5.54864 7.63302L4.13895 7.12041L4.13895 7.12041L5.54864 7.63302ZM3.72587 12.6456L2.31618 12.133L3.72587 12.6456ZM9.17277 16L10.6524 16.2466L10.9435 14.5L9.17277 14.5L9.17277 16ZM8.70017 18.8356L10.1798 19.0822L10.1798 19.0822L8.70017 18.8356ZM11.2061 17.9C10.9228 18.2777 10.4782 18.5 10.0061 18.5L10.0061 21.5C11.4225 21.5 12.7563 20.8331 13.6061 19.7L11.2061 17.9ZM13.4872 14.8586L11.2061 17.9L13.6061 19.7L15.8872 16.6586L13.4872 14.8586ZM15.0061 13C15.0061 13.6164 14.634 14.1493 14.0961 14.3799L15.2782 17.1372C16.8796 16.4507 18.0061 14.8586 18.0061 13L15.0061 13ZM15.0061 8L15.0061 13L18.0061 13L18.0061 8L15.0061 8ZM13.5061 6.5C14.3345 6.5 15.0061 7.17157 15.0061 8L18.0061 8C18.0061 5.51472 15.9914 3.5 13.5061 3.5L13.5061 6.5ZM9.30781 6.5L13.5061 6.5L13.5061 3.5L9.30781 3.5L9.30781 6.5ZM6.95833 8.14564C7.31759 7.15767 8.25655 6.5 9.30781 6.5L9.30781 3.5C6.99503 3.5 4.92933 4.94687 4.13895 7.12041L6.95833 8.14564ZM5.13556 13.1583L6.95833 8.14564L4.13895 7.12041L2.31618 12.133L5.13556 13.1583ZM6.07535 14.5C5.38139 14.5 4.8984 13.8104 5.13556 13.1583L2.31618 12.133C1.36755 14.7418 3.29949 17.5 6.07535 17.5L6.07535 14.5ZM9.17277 14.5L6.07535 14.5L6.07535 17.5L9.17277 17.5L9.17277 14.5ZM10.1798 19.0822L10.6524 16.2466L7.69318 15.7534L7.22058 18.589L10.1798 19.0822ZM9.68656 18.5C9.99553 18.5 10.2306 18.7774 10.1798 19.0822L7.22058 18.589C6.96661 20.1128 8.14171 21.5 9.68656 21.5L9.68656 18.5ZM10.0061 18.5L9.68656 18.5L9.68656 21.5L10.0061 21.5L10.0061 18.5Z" fill="#898C97" mask="url(#path-2-inside-1_387_503)"/>
-</g>
-</g>
-</g>
-</svg>

+ 1 - 1
src/components/bs-icons/upload/index.tsx

@@ -5,6 +5,6 @@ export const UploadIcon = forwardRef<
     SVGSVGElement & { className: any },
     React.PropsWithChildren<{ className?: string }>
 >(({ className, ...props }, ref) => {
-    const _className = 'transition text-gray-950 ' + (className || '')
+    const _className = 'transition text-[#ffd025] ' + (className || '')
     return <Upload ref={ref} {...props} className={_className} />;
 });

BIN
src/components/bs-ui/.DS_Store


+ 58 - 0
src/components/bs-ui/calendar/datePicker.tsx

@@ -0,0 +1,58 @@
+"use client"
+
+import * as React from "react"
+import { CalendarIcon } from "@radix-ui/react-icons"
+import { Button } from "../button"
+import { Calendar } from "../calendar"
+import {
+    Popover,
+    PopoverContent,
+    PopoverTrigger,
+} from "@/components/ui/popover"
+import { cname } from "../utils"
+import { useMemo } from "react"
+import { formatDate } from "@/util/utils"
+
+export function DatePicker({
+    value,
+    placeholder = '',
+    onChange
+}) {
+    const [date, setDate] = React.useState<Date>(value)
+
+    const dateStr = useMemo(() => {
+        return date ? formatDate(date, 'yyyy-MM-dd') : ''
+    }, [date])
+
+    React.useEffect(() => {
+        setDate(value)
+    },[value])
+
+    return (
+        <Popover>
+            <PopoverTrigger asChild>
+                <Button
+                    variant={"outline"}
+                    className={cname(
+                        "w-full justify-start text-left font-normal bg-[#1a1a1a]",
+                        !date && "text-muted-foreground border-[#666] text-[#999]"
+                    )}
+                >
+                    <CalendarIcon className="mr-2 h-4 w-4" />
+                    {dateStr || <span>{placeholder}</span>}
+                </Button>
+            </PopoverTrigger>
+            <PopoverContent className="w-auto p-0" align="start">
+                <Calendar
+                    mode="single"
+                    selected={date}
+                    onSelect={(d) => {
+                        setDate(d)
+                        onChange?.(d)
+                    }}
+                    initialFocus
+                />
+            </PopoverContent>
+        </Popover>
+    )
+}

+ 72 - 0
src/components/bs-ui/calendar/index.tsx

@@ -0,0 +1,72 @@
+"use client"
+
+import * as React from "react"
+import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
+import { DayPicker } from "react-day-picker"
+
+import { buttonVariants } from "@/components/bs-ui/button"
+import { cname } from "../utils"
+
+export type CalendarProps = React.ComponentProps<typeof DayPicker>
+
+function Calendar({
+    className,
+    classNames,
+    showOutsideDays = true,
+    ...props
+}: CalendarProps) {
+    return (
+        <DayPicker
+            showOutsideDays={showOutsideDays}
+            className={cname("p-3", className)}
+            classNames={{
+                months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
+                month: "space-y-4",
+                caption: "flex justify-center pt-1 relative items-center",
+                caption_label: "text-sm font-medium",
+                nav: "space-x-1 flex items-center",
+                nav_button: cname(
+                    buttonVariants({ variant: "outline" }),
+                    "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+                ),
+                nav_button_previous: "absolute left-1",
+                nav_button_next: "absolute right-1",
+                table: "w-full border-collapse space-y-1",
+                head_row: "flex",
+                head_cell:
+                    "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
+                row: "flex w-full mt-2",
+                cell: cname(
+                    "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
+                    props.mode === "range"
+                        ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+                        : "[&:has([aria-selected])]:rounded-md"
+                ),
+                day: cname(
+                    buttonVariants({ variant: "ghost" }),
+                    "h-8 w-8 p-0 font-normal aria-selected:opacity-100"
+                ),
+                day_range_start: "day-range-start",
+                day_range_end: "day-range-end",
+                day_selected:
+                    "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
+                day_today: "bg-accent text-accent-foreground",
+                day_outside:
+                    "day-outside text-muted-foreground opacity-50  aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
+                day_disabled: "text-muted-foreground opacity-50",
+                day_range_middle:
+                    "aria-selected:bg-accent aria-selected:text-accent-foreground",
+                day_hidden: "invisible",
+                ...classNames,
+            }}
+            components={{
+                IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
+                IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
+            }}
+            {...props}
+        />
+    )
+}
+Calendar.displayName = "Calendar"
+
+export { Calendar }

+ 1 - 1
src/components/bs-ui/dialog/index.tsx

@@ -42,7 +42,7 @@ const DialogContent = React.forwardRef<
             {...props}
         >
             {children}
-            <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
+            <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground text-[#fff]">
                 <Cross2Icon className="h-4 w-4" />
                 <span className="sr-only">Close</span>
             </DialogPrimitive.Close>

+ 46 - 0
src/components/bs-ui/input/avator.tsx

@@ -0,0 +1,46 @@
+import { Button } from "../button";
+import { useToast } from "../toast/use-toast";
+import { cname } from "../utils";
+
+// 头像
+export default function Avator({
+    size = 5 * 1024 * 1024,
+    accept = "image/jpeg,image/png",
+    value,
+    className,
+    buttonName = '上传头像',
+    onChange,
+    children
+}) {
+    const { message } = useToast();
+
+    const handleFileChange = (event) => {
+        const file = event.target.files[0];
+        if (file) {
+            const isValidSize = file.size <= size;
+            const isValidType = accept.split(',').some(type => file.type === type);
+
+            const errormgs = []
+            if (!isValidSize) errormgs.push(`文件大小不能超过 ${size / 1024 / 1024}MB`)
+            if (!isValidType) errormgs.push(`文件类型不符合要求:${accept}`)
+
+            errormgs.length ? message({
+                variant: 'error',
+                description: errormgs
+            }) : onChange(file)
+        }
+    };
+
+    return <div className={cname("flex w-full rounded-md gap-4", className)}>
+        {value ? <img src={value} className="max-w-24 max-h-24 rounded-md object-cover" alt="" /> : children}
+        <Button variant="outline" className="relative">
+            {buttonName}
+            <input
+                className=" absolute top-0 left-0 inset-0 w-full h-full opacity-0 cursor-pointer"
+                type="file"
+                accept={accept}
+                onChange={handleFileChange}
+            />
+        </Button>
+    </div>
+};

+ 26 - 4
src/components/bs-ui/input/index.tsx

@@ -3,6 +3,9 @@ import { cname } from "../utils"
 import { SearchIcon } from "../../bs-icons/search"
 import { generateUUID } from "../utils"
 import { MinusCircledIcon } from "@radix-ui/react-icons"
+import { EyeOpenIcon, EyeNoneIcon } from "@radix-ui/react-icons"
+import { useState } from "react"
+
 import sousuo from "../../../assets/npc/sousuo1.png"
 export interface InputProps
     extends React.InputHTMLAttributes<HTMLInputElement> { }
@@ -13,7 +16,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
             <input
                 type={type}
                 className={cname(
-                    "flex h-9 w-full rounded-md border border-input bg-[#FAFBFC] px-3 py-1 text-sm text-[#111] shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ",
+                    "flex h-9 w-full rounded-md  border-input bg-[#FAFBFC] px-3 py-1 text-sm text-[#111] shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[#666] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 ",
                     className
                 )}
                 ref={ref}
@@ -29,15 +32,34 @@ const SearchInput = React.forwardRef<HTMLInputElement, InputProps & { inputClass
     ({ className, inputClassName, iconClassName, ...props }, ref) => {
         return <div className={cname("relative", className)}>
             {/* <SearchIcon className={cname("h-5 w-5 absolute left-2 top-2 text-[#666666]", iconClassName)} /> */}
-            <img src={sousuo} alt="" className="w-[14px] absolute left-[14px] top-[10px]" />
-            <Input type="text" ref={ref} className={cname("w-[244px] h-[34px] pl-[40px] npcInput3", inputClassName)} {...props}></Input>
+            <div className="absolute w-[40px] h-[100%] flex items-center">
+                <img src={sousuo} alt="" className="w-[14px] absolute left-[14px]" />
+            </div>
+            <Input type="text" ref={ref} className={cname("w-[244px] h-[34px] pl-[40px] npcInput2", inputClassName)} {...props}></Input>
         </div>
     }
 )
 
 SearchInput.displayName = "SearchInput"
 
+const PasswordInput = React.forwardRef<HTMLInputElement, InputProps & { inputClassName?: string, iconClassName?: string }>(
+    ({ className, inputClassName, iconClassName, ...props }, ref) => {
+        const [type, setType] = useState('password')
+        const handleShowPwd = () => {
+            type === 'password' ? setType('text') : setType('password')
+        }
+        return <div className={cname("relative flex place-items-center", className)}>
+            <Input type={type} ref={ref} autocomplete="new-password" className={cname("pr-8 bg-[#1a1a1a] text-[#fff] ", inputClassName)} {...props}></Input>
+            {
+                type === 'password'
+                ? <EyeNoneIcon onClick={handleShowPwd} className={cname("absolute right-2 text-[#fff] dark:text-[#fff] cursor-pointer", iconClassName)}/>
+                : <EyeOpenIcon onClick={handleShowPwd} className={cname("absolute right-2 text-[#fff] dark:text-[#fff] cursor-pointer", iconClassName)}/>
+            }
+        </div>
+    }
+)
 
+PasswordInput.displayName = 'PasswordInput'
 
 /**
  * 多行文本
@@ -146,4 +168,4 @@ const InputList = React.forwardRef<HTMLDivElement, InputProps & {
     }
 )
 
-export { Input, SearchInput, Textarea, InputList }
+export { Input, SearchInput, PasswordInput, Textarea, InputList }

+ 1 - 1
src/components/bs-ui/popover/index.tsx

@@ -22,7 +22,7 @@ const PopoverContent = React.forwardRef<
             align={align}
             sideOffset={sideOffset}
             className={cname(
-                "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+                "z-50 w-[280px] rounded-md bg-[#000000] p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 box-shadow",
                 className
             )}
             {...props}

+ 57 - 0
src/components/bs-ui/select/filter.tsx

@@ -0,0 +1,57 @@
+import React from 'react';
+import { Button } from "@/components/bs-ui/button";
+import { Checkbox } from "@/components/bs-ui/checkBox";
+import { useTranslation } from "react-i18next";
+import { SearchInput } from "../../../components/bs-ui/input";
+
+// 定义组件的 props 类型
+interface FilterUserGroupProps {
+  value: string[];
+  options: { id: string;[key: string]: any }[];
+  nameKey?: string;
+  placeholder: string;
+  onChecked: (id: string) => void;
+  search: (event: React.ChangeEvent<HTMLInputElement>) => void;
+  onClearChecked: () => void;
+  onOk: () => void;
+}
+
+const FilterUserGroup: React.FC<FilterUserGroupProps> = ({
+  value = [],
+  options,
+  nameKey = 'name',
+  placeholder,
+  onChecked,
+  search,
+  onClearChecked,
+  onOk
+}) => {
+  const { t } = useTranslation();
+
+  return (
+    <div className="h-full">
+      <div>
+        <SearchInput placeholder={placeholder} className="w-[240px]" onChange={search}></SearchInput>
+      </div>
+      <div className="mt-2 max-h-[260px] min-h-20 overflow-y-auto">
+        {options.map((i) => (
+          <div className="flex items-center space-x-2 text-gray-500 mb-1 xiala" key={i.id}>
+            <Checkbox id={i.id} checked={value.includes(i.id)} onCheckedChange={() => onChecked(i.id)} />
+            <label htmlFor={i.id} className="cursor-pointer text-sm text-[#999]">{i[nameKey]}</label>
+          </div>
+        ))}
+        {options.length === 0 && (
+          <div className="flex items-center justify-center h-[70px]">
+            <Button variant="ghost">{t('build.empty')}</Button>
+          </div>
+        )}
+      </div>
+      <div className="flex justify-end mt-4">
+        <Button variant="ghost" className="px-8 h-8 bg-[#333333] hover:bg-[#333333]" onClick={onClearChecked}>{t('system.reset')}</Button>
+        <Button className="px-8 h-8 bg-[#ffd025] hover:bg-[#ffd025] ml-[20px]" onClick={onOk}>{t('system.confirm')}</Button>
+      </div>
+    </div>
+  );
+}
+
+export default React.memo(FilterUserGroup);

+ 23 - 0
src/components/bs-ui/select/hover.tsx

@@ -0,0 +1,23 @@
+import { Button } from "../button";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../tooltip";
+
+export function SelectHoverItem({ children, ...props }) {
+
+    return <div {...props} className="relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none hover:bg-[#EBF0FF] dark:text-gray-50 dark:hover:bg-[#26231B]" style={{borderRadius:"5px"}}>
+        {children}
+    </div>
+}
+
+export function SelectHover({ triagger, children }) {
+
+    return <TooltipProvider delayDuration={100}>
+        <Tooltip>
+            <TooltipTrigger asChild>
+                {triagger}
+            </TooltipTrigger>
+            <TooltipContent className="text-popover-foreground bg-popover dark:bg-[#1A1A1A] shadow-md">
+                {children}
+            </TooltipContent>
+        </Tooltip>
+    </TooltipProvider>
+};

+ 3 - 3
src/components/bs-ui/select/index.tsx

@@ -23,7 +23,7 @@ const SelectTrigger = React.forwardRef<
     <SelectPrimitive.Trigger
         ref={ref}
         className={cname(
-            "group flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-[#1a1a1a] px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none  disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 data-[placeholder]:text-gray-400",
+            "group flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md  border-input bg-[#1a1a1a] px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none  disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 data-[placeholder]:text-gray-400 border-[#666] text-[#999]",
             className
         )}
         {...props}
@@ -79,7 +79,7 @@ const SelectContent = React.forwardRef<
         <SelectPrimitive.Content
             ref={ref}
             className={cname(
-                "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md box-shadow border-[#DEE3EF] bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+                "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md box-shadow border-[#0d0d0d] bg-[#0d0d0d] text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
                 position === "popper" &&
                 "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
                 className
@@ -124,7 +124,7 @@ const SelectItem = React.forwardRef<
     <SelectPrimitive.Item
         ref={ref}
         className={cname(
-            "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-[#1a1a1a] focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+            "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-[#2e2406] focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
             className
         )}
         {...props}

+ 150 - 50
src/components/bs-ui/select/multi.tsx

@@ -1,15 +1,19 @@
 import { CheckIcon, Cross1Icon } from "@radix-ui/react-icons"
-import React, { useEffect, useRef } from "react"
+import React, { useEffect, useMemo, useRef, useState } from "react"
 import { Select, SelectContent, SelectItem, SelectTrigger } from "."
 import { Badge } from "../badge"
 import { SearchInput } from "../input"
+import { useDebounce } from "../utils"
 
-const MultiItem = ({ active, children, value, onClick }) => {
+const MultiItem: React.FC<
+    { active: boolean; children: React.ReactNode; value: string; onClick: (value: string, label: string) => void }
+> = ({ active, children, value, onClick }) => {
 
-    return <div key={value}
-        className={`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 mb-1 text-sm outline-none hover:bg-[#1a1a1a] hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 break-all 
-    ${active && 'bg-[#1a1a1a]'}`}
-        onClick={() => { onClick(value) }}
+    return <div
+        key={value}
+        className={`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 mb-1 text-sm outline-none hover:bg-[] dark:hover:bg-[#2e2406] hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 break-all 
+    ${active && 'bg-[#EBF0FF] dark:bg-[#2e2406]'}`}
+        onClick={() => { onClick(value, children as string) }}
     >
         <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
             {active && <CheckIcon className="h-4 w-4"></CheckIcon>}
@@ -17,21 +21,45 @@ const MultiItem = ({ active, children, value, onClick }) => {
         {children}
     </div>
 }
+interface Option {
+    label: string;
+    value: string;
+}
+
+interface BaseProps<T> {
+    /** 多选 */
+    multiple?: boolean;
+    disabled?: boolean;
+    className?: string;
+    options: Option[];
+    children?: React.ReactNode;
+    placeholder?: string;
+    searchPlaceholder?: string;
+    /** 锁定不可修改的值 */
+    lockedValues?: string[];
+    onLoad?: () => void;
+    onSearch?: (name: string) => void;
+    onChange?: (value: T) => void;
+}
 
+// onScrollLoad有值表示开启分页、异步检索
+interface ScrollLoadProps extends BaseProps<Option[]> {
+    onScrollLoad: (name: string) => void;
+    value?: Option[];
+    defaultValue?: Option[];
+}
 
-interface IProps {
-    className?: string,
-    options: { label: string, value: string }[],
-    value?: string[],
-    defaultValue?: string[],
-    children?: React.ReactNode,
-    placeholder?: string,
-    searchPlaceholder?: string,
-    lockedValues?: string[],
-    onChange?: (value: string[]) => void
+interface NonScrollLoadProps extends BaseProps<string[]> {
+    onScrollLoad?: undefined;
+    value?: string[];
+    defaultValue?: string[];
 }
+
+type IProps = ScrollLoadProps | NonScrollLoadProps;
+
 // 临时用 andt 设计方案封装组件
 const MultiSelect = ({
+    multiple = false,
     className,
     value = [],
     defaultValue = [],
@@ -40,13 +68,15 @@ const MultiSelect = ({
     placeholder = '',
     searchPlaceholder = '',
     lockedValues = [],
+    onSearch,
+    onLoad,
+    onScrollLoad,
     onChange, ...props
 }: IProps) => {
 
     const [values, setValues] = React.useState(defaultValue)
     const [optionFilter, setOptionFilter] = React.useState(options)
-
-
+    const [created, creatInput] = useState(false)
     const inputRef = useRef(null)
 
     useEffect(() => {
@@ -54,75 +84,145 @@ const MultiSelect = ({
     }, [value])
 
     useEffect(() => {
-        setOptionFilter(options)
-        if (inputRef.current) {
-            inputRef.current.value = ''
-        }
-    }, [options])
-    // delete
+        // if (onScrollLoad) {
+        setOptionFilter(options);
+        // }
+    }, [options]);
+
+    // delete 
     const handleDelete = (value: string) => {
-        const newValues = values.filter((item) => {
-            return item !== value
+        const newValues = (values as any[]).filter((item) => {
+            const _value = onScrollLoad ? (item as Option).value : item;
+            return _value !== value
         })
         setValues(newValues)
         onChange?.(newValues)
     }
     // add
-    const handleSwitch = (value: string) => {
+    const handleSwitch = (value: string, label: string) => {
         if (lockedValues.includes(value)) {
             return
         }
-        if (values.includes(value)) {
-            const newValues = values.filter((item) => {
-                return item !== value
-            })
-            setValues(newValues)
-            onChange?.(newValues)
+
+        const updateValues = (newValues: any) => {
+            setValues(newValues);
+            onChange?.(newValues);
+        };
+
+        // 单选
+        if (!multiple) {
+            const newValues = onScrollLoad ? [{ label, value }] : [value]
+            updateValues(newValues);
+            return
+        }
+
+        if (onScrollLoad) {
+            const newValues = (values as Option[]).some(item => item.value === value)
+                ? (values as Option[]).filter(item => item.value !== value)
+                : [...(values as Option[]), { label, value }];
+            updateValues(newValues);
         } else {
-            const _newValues = [...values, value]
-            setValues(_newValues)
-            onChange?.(_newValues)
+            const newValues = (values as string[]).includes(value)
+                ? (values as string[]).filter(item => item !== value)
+                : [...(values as string[]), value];
+            updateValues(newValues);
         }
     }
 
     // search
-    const handleSearch = (e) => {
+    const handleSearch = useDebounce((e) => {
         const newValues = options.filter((item) => {
             return item.label.toLowerCase().indexOf(e.target.value.toLowerCase()) !== -1
         })
         setOptionFilter(newValues)
-    }
-    return <Select {...props} required onOpenChange={(e) => !e && setOptionFilter(options)}>
-        <SelectTrigger className="mt-2 h-auto bg-[#1a1a1a]">
+        onSearch?.(inputRef.current?.value || '')
+    }, 500, false)
+
+    // scroll laod
+    const footerRef = useRef(null)
+    useEffect(function () {
+        if (!created) return
+        if (!footerRef.current) return
+        if (!onScrollLoad) return // 不绑定滚动事件
+
+        const observer = new IntersectionObserver((entries) => {
+            entries.forEach(entry => {
+                if (entry.isIntersecting) {
+                    // console.log('div is in the viewport!');
+                    onScrollLoad?.(inputRef.current?.value || '')
+                }
+            });
+        }, {
+            // root: null, // 视口
+            rootMargin: '0px', // 视口的边距
+            threshold: 0.1 // 目标元素超过视口的10%即触发回调
+        });
+
+        // 开始观察目标元素
+        observer.observe(footerRef.current);
+
+        return () => observer.unobserve(footerRef.current);
+    }, [created])
+
+    return <Select
+        {...props}
+        required
+        onOpenChange={(e) => {
+            creatInput(e);
+            if (!e) {
+                onLoad?.();
+                setOptionFilter(options);
+            }
+        }}
+    >
+        <SelectTrigger className="h-auto">
+            {
+                !multiple && (values.length ? <span>{onScrollLoad ? (values[0] as Option).label : options.find(op => op.value === values[0])?.label}</span> : placeholder)
+            }
             {
-                values.length
-                    ? <div className="flex flex-wrap">
+                multiple && (values.length ? (
+                    onScrollLoad ? <div className="flex flex-wrap">
+                        {
+                            values.map(item =>
+                                <Badge onPointerDown={(e) => e.stopPropagation()} key={item.value} className="flex whitespace-normal items-center gap-1 select-none bg-[#261F08] text-[#CCA831] hover:bg-[#261F08] m-[2px]">
+                                    {item.label}
+                                    {lockedValues.includes(item.value) || <Cross1Icon className="h-3 w-3" onClick={() => handleDelete(item.value)}></Cross1Icon>}
+                                </Badge>
+                            )
+                        }
+                    </div> : <div className="flex flex-wrap">
                         {
-                            options.filter(option => values.includes(option.value)).map(option =>
-                                <Badge onPointerDown={(e) => e.stopPropagation()} key={option.value} className="flex whitespace-normal items-center gap-1 select-none bg-primary/20 text-primary hover:bg-primary/15 m-[2px]">
+                            options.filter(option => (values as string[]).includes(option.value)).map(option =>
+                                <Badge onPointerDown={(e) => e.stopPropagation()} key={option.value} className="flex whitespace-normal items-center gap-1 select-none bg-[#261F08] text-[#CCA831] hover:bg-[#261F08] m-[2px] break-all">
                                     {option.label}
                                     {lockedValues.includes(option.value) || <Cross1Icon className="h-3 w-3" onClick={() => handleDelete(option.value)}></Cross1Icon>}
                                 </Badge>
                             )
                         }
-                    </div>
-                    : placeholder
+                    </div>)
+                    : placeholder)
             }
         </SelectTrigger>
-        <SelectContent className={className}
+        <SelectContent
+            className={className}
             headNode={
                 <div className="p-2">
-                    <SearchInput ref={inputRef} inputClassName="h-8" placeholder={searchPlaceholder} onChange={handleSearch} iconClassName="w-4 h-4" />
+                    <SearchInput ref={inputRef} inputClassName="w-[98%] h-8 dark:border-gray-700" placeholder={searchPlaceholder} onChange={handleSearch} iconClassName="w-4 h-4" />
                 </div>
             }
             footerNode={children}
         >
             <div className="mt-2">
                 {
-                    optionFilter.map((item, index) => (
-                        <MultiItem active={values.includes(item.value)} value={item.value} onClick={handleSwitch}>{item.label}</MultiItem>
+                    optionFilter.map((item) => (
+                        <MultiItem
+                            active={values.some(val => val === item.value || val.value === item.value)}
+                            value={item.value}
+                            onClick={handleSwitch}
+                        >{item.label}</MultiItem>
                     ))
                 }
+                <div ref={footerRef} style={{ height: 20 }}></div>
             </div>
         </SelectContent>
     </Select>

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません