cx-chat 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/.cursor/rules/i18n-cn-gloss-comments.mdc +31 -0
  2. package/.cursor/rules/list-page-view-pageconfig.mdc +32 -0
  3. package/.cursor/rules/no-over-defensive-programming.mdc +90 -0
  4. package/.cursor/rules/requirement-description-for-agent.mdc +33 -0
  5. package/.cursor/rules/use-showToast-not-antd-message.mdc +28 -0
  6. package/.docker/Dockerfile +7 -0
  7. package/.env +9 -0
  8. package/.env.development +7 -0
  9. package/.env.production +7 -0
  10. package/.gitlab-ci/docker-build.yaml +28 -0
  11. package/.gitlab-ci/k8s-deploy-dev-master.yaml +42 -0
  12. package/.gitlab-ci/npm-build.yaml +17 -0
  13. package/.gitlab-ci.yml +8 -0
  14. package/.k8s/0-namespace.yaml +6 -0
  15. package/.k8s/1-configmap-web.yaml +7 -0
  16. package/.k8s/1-nginx-conf-dev.yaml +110 -0
  17. package/.k8s/2-deployment.yaml +27 -0
  18. package/.k8s/3-service.yaml +16 -0
  19. package/.k8s/4-ingress-dev.yaml +30 -0
  20. package/.lingma/rules/use-showToast-not-antd-message.md +34 -0
  21. package/.nginx/nginx.conf +52 -0
  22. package/.prettierrc +9 -0
  23. package/README.md +1 -0
  24. package/eslint.config.js +32 -0
  25. package/index.html +13 -0
  26. package/package.json +67 -0
  27. package/postcss.config.js +6 -0
  28. package/public/favicon.ico +0 -0
  29. package/public/vite.svg +1 -0
  30. package/src/App.tsx +96 -0
  31. package/src/_doc/0.docs-overview.md +28 -0
  32. package/src/_doc/cx-ui/0.docs-overview.md +30 -0
  33. package/src/_doc/cx-ui/comp.1.cx-ui-overview.md +82 -0
  34. package/src/_doc/cx-ui/comp.2.cx-modal.md +82 -0
  35. package/src/_doc/cx-ui/comp.3.cx-button.md +89 -0
  36. package/src/_doc/cx-ui/comp.4.cx-form.md +72 -0
  37. package/src/_doc/cx-ui/comp.5.cx-fields.md +76 -0
  38. package/src/_doc/cx-ui/comp.6.cx-tag.md +57 -0
  39. package/src/_doc/cx-ui/comp.7.cx-empty-state.md +29 -0
  40. package/src/_doc/meta/0.docs-overview.md +24 -0
  41. package/src/_doc/meta/comp.1.enum-runtime.md +33 -0
  42. package/src/_doc/meta/comp.2.dict-runtime.md +39 -0
  43. package/src/_doc/router/0.docs-overview.md +14 -0
  44. package/src/_doc/router/guide.1.menu-component-config.md +181 -0
  45. package/src/_doc/router/guide.2.router-auto-registration.md +114 -0
  46. package/src/_doc/table-view/0.docs-overview.md +30 -0
  47. package/src/_doc/table-view/comp.1.table-view.md +542 -0
  48. package/src/_doc/table-view/props.1.create-table-view-config.md +193 -0
  49. package/src/_doc/table-view/props.2.table-view-search-fields.md +106 -0
  50. package/src/api/_mock/README.md +340 -0
  51. package/src/api/_mock/api.ts +1642 -0
  52. package/src/api/_mock/bundle-shim.ts +16 -0
  53. package/src/api/_mock/handler-shim.ts +6 -0
  54. package/src/api/_mock/handler.ts +458 -0
  55. package/src/api/_mock/index.ts +711 -0
  56. package/src/api/_mock/interceptor.ts +15 -0
  57. package/src/api/_mock/mod.ts +12 -0
  58. package/src/api/_mock/utils.ts +65 -0
  59. package/src/api/base/memory.js +24 -0
  60. package/src/api/chat.js +210 -0
  61. package/src/api/common/auth.js +70 -0
  62. package/src/api/menus/business-rules.js +76 -0
  63. package/src/api/menus/feedback.js +102 -0
  64. package/src/api/menus/knowledge.js +159 -0
  65. package/src/api/menus/model-metadata/manage.js +70 -0
  66. package/src/api/menus/model-metadata/role.js +50 -0
  67. package/src/api/menus/model-metadata/training-detail-mock-data.js +569 -0
  68. package/src/api/menus/model-metadata/training.js +28 -0
  69. package/src/api/menus/skill.js +40 -0
  70. package/src/api/system/agent-config.js +16 -0
  71. package/src/api/system/department.js +94 -0
  72. package/src/api/system/dict.js +86 -0
  73. package/src/api/system/menu.js +37 -0
  74. package/src/api/system/permission.js +26 -0
  75. package/src/api/system/role.js +34 -0
  76. package/src/api/system/sys-config.js +16 -0
  77. package/src/api/system/sys-log.js +17 -0
  78. package/src/api/system/user.js +75 -0
  79. package/src/api/upload.js +39 -0
  80. package/src/assets/react.svg +1 -0
  81. package/src/components/auth/current-user-avatar.tsx +77 -0
  82. package/src/components/common/code-view.tsx +149 -0
  83. package/src/components/common/detail-link.tsx +67 -0
  84. package/src/components/common/error-boundary.tsx +98 -0
  85. package/src/components/common/language-switcher.tsx +91 -0
  86. package/src/components/common/lite-table/index.tsx +135 -0
  87. package/src/components/common/md-editor.tsx +126 -0
  88. package/src/components/common/modal/confirm-dialog.tsx +113 -0
  89. package/src/components/common/modal/dep-user-select-multi.tsx +324 -0
  90. package/src/components/common/modal/dep-user-select.tsx +249 -0
  91. package/src/components/common/modal/user-select-multi.tsx +266 -0
  92. package/src/components/common/pagination.tsx +472 -0
  93. package/src/components/common/path.tsx +175 -0
  94. package/src/components/common/system-logo-mark.tsx +48 -0
  95. package/src/components/cx-ui/button/index.less +208 -0
  96. package/src/components/cx-ui/button/index.tsx +611 -0
  97. package/src/components/cx-ui/checkbox/index.tsx +78 -0
  98. package/src/components/cx-ui/date-picker/index.less +17 -0
  99. package/src/components/cx-ui/date-picker/index.tsx +193 -0
  100. package/src/components/cx-ui/drawer/index.tsx +47 -0
  101. package/src/components/cx-ui/empty-state/index.tsx +20 -0
  102. package/src/components/cx-ui/floating-shell/CxFloatingShell.tsx +89 -0
  103. package/src/components/cx-ui/floating-shell/cx-floating-shell.less +283 -0
  104. package/src/components/cx-ui/floating-shell/has-floating-value.ts +41 -0
  105. package/src/components/cx-ui/form/CxForm.tsx +15 -0
  106. package/src/components/cx-ui/form/index.tsx +20 -0
  107. package/src/components/cx-ui/form-item/index.less +26 -0
  108. package/src/components/cx-ui/form-item/index.tsx +36 -0
  109. package/src/components/cx-ui/index.ts +70 -0
  110. package/src/components/cx-ui/input/auto-complete.tsx +134 -0
  111. package/src/components/cx-ui/input/index.tsx +259 -0
  112. package/src/components/cx-ui/input-number/index.jsx +66 -0
  113. package/src/components/cx-ui/modal/index.jsx +212 -0
  114. package/src/components/cx-ui/modal/index.less +144 -0
  115. package/src/components/cx-ui/modal/useCxModal.ts +125 -0
  116. package/src/components/cx-ui/multi-select/index.jsx +74 -0
  117. package/src/components/cx-ui/multi-select/index.less +40 -0
  118. package/src/components/cx-ui/multi-select/index2.tsx +361 -0
  119. package/src/components/cx-ui/radio/index.tsx +33 -0
  120. package/src/components/cx-ui/range-picker/index.less +65 -0
  121. package/src/components/cx-ui/range-picker/index.tsx +219 -0
  122. package/src/components/cx-ui/select/index.less +34 -0
  123. package/src/components/cx-ui/select/index.tsx +196 -0
  124. package/src/components/cx-ui/skeleton/index.tsx +12 -0
  125. package/src/components/cx-ui/steps/index.tsx +14 -0
  126. package/src/components/cx-ui/styles/_tokens.less +79 -0
  127. package/src/components/cx-ui/styles/index.less +246 -0
  128. package/src/components/cx-ui/switch/index.less +106 -0
  129. package/src/components/cx-ui/switch/index.tsx +120 -0
  130. package/src/components/cx-ui/table/index.less +160 -0
  131. package/src/components/cx-ui/table/index.tsx +152 -0
  132. package/src/components/cx-ui/tabs/index.less +15 -0
  133. package/src/components/cx-ui/tabs/index.tsx +34 -0
  134. package/src/components/cx-ui/tag/index.less +51 -0
  135. package/src/components/cx-ui/tag/index.tsx +140 -0
  136. package/src/components/cx-ui/timeline/index.tsx +14 -0
  137. package/src/components/cx-ui/tooltip/index.tsx +67 -0
  138. package/src/components/cx-ui/tree/index.tsx +193 -0
  139. package/src/components/cx-ui/tree-select/index.jsx +91 -0
  140. package/src/components/cx-ui/tree-select/index.less +27 -0
  141. package/src/components/cx-ui/upload-file/index.less +223 -0
  142. package/src/components/cx-ui/upload-file/index.tsx +640 -0
  143. package/src/components/cx-ui/upload-img/index.tsx +291 -0
  144. package/src/components/layout/components/Header.tsx +216 -0
  145. package/src/components/layout/components/Sidebar.tsx +717 -0
  146. package/src/components/layout/index.tsx +95 -0
  147. package/src/components/table-view/components/search-area.tsx +411 -0
  148. package/src/components/table-view/components/table-view-config.tsx +528 -0
  149. package/src/components/table-view/components/table-view.types.ts +478 -0
  150. package/src/components/table-view/components/tree-api-normalize.ts +38 -0
  151. package/src/components/table-view/components/tree-data-annotate.ts +31 -0
  152. package/src/components/table-view/components/tree-sidebar.tsx +74 -0
  153. package/src/components/table-view/index.tsx +61 -0
  154. package/src/components/table-view/list-page-view.tsx +1049 -0
  155. package/src/components/table-view/select-table-view.tsx +1094 -0
  156. package/src/components/table-view/styles/select-table-view.less +51 -0
  157. package/src/config/default-system-name.ts +9 -0
  158. package/src/config/system.ts +165 -0
  159. package/src/constants/countryCodes.ts +3 -0
  160. package/src/contexts/AuthContext.tsx +256 -0
  161. package/src/contexts/ChatContext.tsx +839 -0
  162. package/src/contexts/MenuContext.tsx +62 -0
  163. package/src/contexts/ToastContext.tsx +181 -0
  164. package/src/hooks/useCopyToClipboard.ts +47 -0
  165. package/src/hooks/useModalSubmit.ts +104 -0
  166. package/src/hooks/useRouter.ts +240 -0
  167. package/src/hooks/useStepForm.ts +46 -0
  168. package/src/hooks/useStickyHeader.ts +42 -0
  169. package/src/hooks/useThreadActions.ts +105 -0
  170. package/src/hooks/useUserPreferences.ts +101 -0
  171. package/src/http/axios.js +372 -0
  172. package/src/http/mock.interceptor.ts +9 -0
  173. package/src/http/obfuscationKey.ts +41 -0
  174. package/src/i18n.ts +60 -0
  175. package/src/index.js +1 -0
  176. package/src/index.less +169 -0
  177. package/src/locales/en/auth.ts +70 -0
  178. package/src/locales/en/base/memory.ts +28 -0
  179. package/src/locales/en/base/settings.ts +41 -0
  180. package/src/locales/en/chat.ts +40 -0
  181. package/src/locales/en/common.ts +173 -0
  182. package/src/locales/en/enum.ts +27 -0
  183. package/src/locales/en/menus/business-rules.ts +48 -0
  184. package/src/locales/en/menus/feedback.ts +62 -0
  185. package/src/locales/en/menus/knowledge.ts +120 -0
  186. package/src/locales/en/menus/model-metadata/index.ts +10 -0
  187. package/src/locales/en/menus/model-metadata/manage.ts +151 -0
  188. package/src/locales/en/menus/model-metadata/role.ts +48 -0
  189. package/src/locales/en/menus/model-metadata/training.ts +65 -0
  190. package/src/locales/en/menus/skill.ts +34 -0
  191. package/src/locales/en/system/agent-config.ts +34 -0
  192. package/src/locales/en/system/department.ts +68 -0
  193. package/src/locales/en/system/dict.ts +44 -0
  194. package/src/locales/en/system/menu.ts +45 -0
  195. package/src/locales/en/system/permission.ts +89 -0
  196. package/src/locales/en/system/role.ts +25 -0
  197. package/src/locales/en/system/sys-config.ts +33 -0
  198. package/src/locales/en/system/sys-log.ts +38 -0
  199. package/src/locales/en/system/user.ts +113 -0
  200. package/src/locales/en.ts +68 -0
  201. package/src/locales/zh/auth.ts +70 -0
  202. package/src/locales/zh/base/memory.ts +29 -0
  203. package/src/locales/zh/base/settings.ts +41 -0
  204. package/src/locales/zh/chat.ts +47 -0
  205. package/src/locales/zh/common.ts +178 -0
  206. package/src/locales/zh/enum.ts +28 -0
  207. package/src/locales/zh/menus/business-rules.ts +47 -0
  208. package/src/locales/zh/menus/feedback.ts +62 -0
  209. package/src/locales/zh/menus/knowledge.ts +117 -0
  210. package/src/locales/zh/menus/model-metadata/index.ts +10 -0
  211. package/src/locales/zh/menus/model-metadata/manage.ts +151 -0
  212. package/src/locales/zh/menus/model-metadata/role.ts +47 -0
  213. package/src/locales/zh/menus/model-metadata/training.ts +64 -0
  214. package/src/locales/zh/menus/skill.ts +34 -0
  215. package/src/locales/zh/system/agent-config.ts +33 -0
  216. package/src/locales/zh/system/department.ts +69 -0
  217. package/src/locales/zh/system/dict.ts +44 -0
  218. package/src/locales/zh/system/menu.ts +47 -0
  219. package/src/locales/zh/system/permission.ts +94 -0
  220. package/src/locales/zh/system/role.ts +25 -0
  221. package/src/locales/zh/system/sys-config.ts +32 -0
  222. package/src/locales/zh/system/sys-log.ts +38 -0
  223. package/src/locales/zh/system/user.ts +114 -0
  224. package/src/locales/zh.ts +67 -0
  225. package/src/main.tsx +50 -0
  226. package/src/meta/const/index.ts +40 -0
  227. package/src/meta/index-dict.ts +56 -0
  228. package/src/meta/index-enum.ts +95 -0
  229. package/src/meta/index.ts +14 -0
  230. package/src/meta/module/dict-data/runtime.ts +199 -0
  231. package/src/meta/module/dict-data/types.ts +17 -0
  232. package/src/meta/module/enum-data/runtime.ts +75 -0
  233. package/src/meta/module/enum-data/types.ts +18 -0
  234. package/src/router/index.tsx +312 -0
  235. package/src/styles/AntdThemeProvider.tsx +40 -0
  236. package/src/styles/antd-theme.ts +20 -0
  237. package/src/styles/global.less +107 -0
  238. package/src/styles/variable.less +103 -0
  239. package/src/types/feedback.ts +43 -0
  240. package/src/types/index.ts +85 -0
  241. package/src/types/menu.ts +43 -0
  242. package/src/utils/aesUtil.ts +123 -0
  243. package/src/utils/chatUtils.ts +524 -0
  244. package/src/utils/cn.ts +6 -0
  245. package/src/utils/crypto.ts +164 -0
  246. package/src/utils/date.ts +72 -0
  247. package/src/utils/file-icons.tsx +79 -0
  248. package/src/utils/index.ts +168 -0
  249. package/src/utils/markdown-math-plugins.ts +21 -0
  250. package/src/utils/menuI18n.ts +305 -0
  251. package/src/utils/menuRouteRegistry.ts +78 -0
  252. package/src/utils/permission-crud.ts +147 -0
  253. package/src/utils/routeConfig.ts +350 -0
  254. package/src/utils/storage.ts +135 -0
  255. package/src/utils/toastBridge.ts +26 -0
  256. package/src/utils/url.ts +38 -0
  257. package/src/utils/validation.ts +16 -0
  258. package/src/views/auth/auth-code/index.less +169 -0
  259. package/src/views/auth/auth-code/index.module.less +174 -0
  260. package/src/views/auth/auth-code/index.tsx +233 -0
  261. package/src/views/auth/login.tsx +498 -0
  262. package/src/views/auth/register.tsx +388 -0
  263. package/src/views/base/memory/index.tsx +136 -0
  264. package/src/views/base/memory/modal/detail-modal.tsx +89 -0
  265. package/src/views/base/memory/modal/submit-modal.tsx +134 -0
  266. package/src/views/base/settings/index.tsx +657 -0
  267. package/src/views/chat/chat.less +323 -0
  268. package/src/views/chat/components/chat-input.tsx +298 -0
  269. package/src/views/chat/components/header-thread-title.tsx +210 -0
  270. package/src/views/chat/components/message-list/content-answer.tsx +100 -0
  271. package/src/views/chat/components/message-list/content-question.tsx +18 -0
  272. package/src/views/chat/components/message-list/index.tsx +520 -0
  273. package/src/views/chat/components/message-list/message-item.tsx +199 -0
  274. package/src/views/chat/components/message-list/preparation-demo-items.ts +147 -0
  275. package/src/views/chat/components/message-list/preparation-steps.tsx +506 -0
  276. package/src/views/chat/components/message-list/suggestion-list.tsx +36 -0
  277. package/src/views/chat/components/message-list/thinking-process.tsx +49 -0
  278. package/src/views/chat/components/message-list/toolbar.tsx +224 -0
  279. package/src/views/chat/components/message-list/use-message-list-scroll.ts +214 -0
  280. package/src/views/chat/components/references-knowledge/context.tsx +57 -0
  281. package/src/views/chat/components/references-knowledge/index.ts +9 -0
  282. package/src/views/chat/components/references-knowledge/modal/knowledge-detail-drawer.tsx +556 -0
  283. package/src/views/chat/components/references-knowledge/modal/knowledge-doc-detail-drawer.tsx +529 -0
  284. package/src/views/chat/components/references-knowledge/panel.tsx +115 -0
  285. package/src/views/chat/hooks/use-chat-common.ts +19 -0
  286. package/src/views/chat/index-session.tsx +647 -0
  287. package/src/views/chat/index.tsx +127 -0
  288. package/src/views/page-error/401.tsx +56 -0
  289. package/src/views/page-error/404.tsx +56 -0
  290. package/src/views/page-menus/business-rules/index.tsx +376 -0
  291. package/src/views/page-menus/business-rules/modal/detail-modal.tsx +186 -0
  292. package/src/views/page-menus/business-rules/modal/scope-modal.tsx +272 -0
  293. package/src/views/page-menus/business-rules/modal/submit-modal.tsx +142 -0
  294. package/src/views/page-menus/feedback/components/feedback-dataset-list.tsx +471 -0
  295. package/src/views/page-menus/feedback/index.tsx +166 -0
  296. package/src/views/page-menus/feedback/modal/export-feedback-modal.tsx +367 -0
  297. package/src/views/page-menus/knowledge/components/doc-editor-by-type.tsx +32 -0
  298. package/src/views/page-menus/knowledge/components/doc-editor-type-file.tsx +330 -0
  299. package/src/views/page-menus/knowledge/detail.tsx +600 -0
  300. package/src/views/page-menus/knowledge/index.tsx +337 -0
  301. package/src/views/page-menus/knowledge/modal/detail-modal.tsx +618 -0
  302. package/src/views/page-menus/knowledge/modal/doc-detail-modal.tsx +550 -0
  303. package/src/views/page-menus/knowledge/modal/doc-parse.ts +99 -0
  304. package/src/views/page-menus/knowledge/modal/doc-submit-modal.tsx +349 -0
  305. package/src/views/page-menus/knowledge/modal/doc-type-picker-modal.tsx +88 -0
  306. package/src/views/page-menus/knowledge/modal/knowledge-user-select-modal.tsx +283 -0
  307. package/src/views/page-menus/knowledge/modal/submit-modal.tsx +179 -0
  308. package/src/views/page-menus/model-metadata/manage/components/metadata-detail-schema-tab.tsx +114 -0
  309. package/src/views/page-menus/model-metadata/manage/components/step1-basic-info.tsx +232 -0
  310. package/src/views/page-menus/model-metadata/manage/components/step2-schema.tsx +316 -0
  311. package/src/views/page-menus/model-metadata/manage/components/step3-permissions.tsx +134 -0
  312. package/src/views/page-menus/model-metadata/manage/components/step4-documents.tsx +134 -0
  313. package/src/views/page-menus/model-metadata/manage/components/step5-example-sql.tsx +101 -0
  314. package/src/views/page-menus/model-metadata/manage/components/submit-add.tsx +338 -0
  315. package/src/views/page-menus/model-metadata/manage/components/submit-edit.tsx +276 -0
  316. package/src/views/page-menus/model-metadata/manage/detail.tsx +298 -0
  317. package/src/views/page-menus/model-metadata/manage/hooks/model-metadata-submit-shared.ts +113 -0
  318. package/src/views/page-menus/model-metadata/manage/hooks/use-model-metadata-item-state.ts +20 -0
  319. package/src/views/page-menus/model-metadata/manage/index.tsx +304 -0
  320. package/src/views/page-menus/model-metadata/manage/modal/components/table-schema.ts +374 -0
  321. package/src/views/page-menus/model-metadata/manage/modal/components/use-table-detail-tabs.tsx +151 -0
  322. package/src/views/page-menus/model-metadata/manage/modal/components/use-table-submit-tabs.tsx +423 -0
  323. package/src/views/page-menus/model-metadata/manage/modal/detail-modal.tsx +218 -0
  324. package/src/views/page-menus/model-metadata/manage/modal/submit-modal.tsx +261 -0
  325. package/src/views/page-menus/model-metadata/manage/modal/table-detail-modal.tsx +196 -0
  326. package/src/views/page-menus/model-metadata/manage/modal/table-submit-modal.tsx +229 -0
  327. package/src/views/page-menus/model-metadata/manage/submit.tsx +31 -0
  328. package/src/views/page-menus/model-metadata/role/index.tsx +207 -0
  329. package/src/views/page-menus/model-metadata/role/modal/detail-modal.tsx +97 -0
  330. package/src/views/page-menus/model-metadata/role/modal/role-assign-users-modal.tsx +254 -0
  331. package/src/views/page-menus/model-metadata/role/modal/role-assign-users-panel.tsx +393 -0
  332. package/src/views/page-menus/model-metadata/role/modal/role-assign-users-utils.ts +120 -0
  333. package/src/views/page-menus/model-metadata/role/modal/role-permission-assign-panel.tsx +698 -0
  334. package/src/views/page-menus/model-metadata/role/modal/role-permission-modal.tsx +237 -0
  335. package/src/views/page-menus/model-metadata/role/modal/submit-modal.tsx +135 -0
  336. package/src/views/page-menus/model-metadata/training/components/detail-records/index.ts +4 -0
  337. package/src/views/page-menus/model-metadata/training/components/detail-records/node-card.tsx +72 -0
  338. package/src/views/page-menus/model-metadata/training/components/detail-records/summary-lines.ts +196 -0
  339. package/src/views/page-menus/model-metadata/training/components/detail-records/summary-list.tsx +153 -0
  340. package/src/views/page-menus/model-metadata/training/components/detail-records/timeline.tsx +103 -0
  341. package/src/views/page-menus/model-metadata/training/components/detail-records/types.ts +82 -0
  342. package/src/views/page-menus/model-metadata/training/detail.tsx +159 -0
  343. package/src/views/page-menus/model-metadata/training/index.tsx +236 -0
  344. package/src/views/page-menus/model-metadata/training/modal/update-detail-modal.tsx +154 -0
  345. package/src/views/page-menus/skill/index.tsx +201 -0
  346. package/src/views/page-menus/skill/modal/detail-modal.tsx +156 -0
  347. package/src/views/page-menus/skill/modal/submit-modal.tsx +214 -0
  348. package/src/views/page-system/agent-config/index.tsx +370 -0
  349. package/src/views/page-system/department/departmentFormShared.ts +36 -0
  350. package/src/views/page-system/department/index.tsx +541 -0
  351. package/src/views/page-system/department/modal/detail-modal.tsx +94 -0
  352. package/src/views/page-system/department/modal/member-role-modal.tsx +128 -0
  353. package/src/views/page-system/department/modal/submit-modal.tsx +265 -0
  354. package/src/views/page-system/dict/index.tsx +440 -0
  355. package/src/views/page-system/dict/modal/cate-submit-modal.tsx +315 -0
  356. package/src/views/page-system/dict/modal/submit-modal.tsx +184 -0
  357. package/src/views/page-system/logs/components/index.ts +3 -0
  358. package/src/views/page-system/logs/components/log-message-demo.tsx +30 -0
  359. package/src/views/page-system/logs/components/log-message-stream.ts +184 -0
  360. package/src/views/page-system/logs/components/message-list/content-answer.tsx +100 -0
  361. package/src/views/page-system/logs/components/message-list/content-question.tsx +18 -0
  362. package/src/views/page-system/logs/components/message-list/index.tsx +515 -0
  363. package/src/views/page-system/logs/components/message-list/message-item.tsx +193 -0
  364. package/src/views/page-system/logs/components/message-list/preparation-demo-items.ts +147 -0
  365. package/src/views/page-system/logs/components/message-list/preparation-steps.tsx +506 -0
  366. package/src/views/page-system/logs/components/message-list/suggestion-list.tsx +36 -0
  367. package/src/views/page-system/logs/components/message-list/thinking-process.tsx +49 -0
  368. package/src/views/page-system/logs/components/message-list/toolbar.tsx +134 -0
  369. package/src/views/page-system/logs/components/message-list/use-message-list-scroll.ts +214 -0
  370. package/src/views/page-system/logs/components/message-modal.tsx +239 -0
  371. package/src/views/page-system/logs/index.tsx +132 -0
  372. package/src/views/page-system/logs/modal/detail-modal.tsx +157 -0
  373. package/src/views/page-system/menu/components/menuFormShared.ts +283 -0
  374. package/src/views/page-system/menu/index.less +12 -0
  375. package/src/views/page-system/menu/index.tsx +410 -0
  376. package/src/views/page-system/menu/modal/icon-modal.less +51 -0
  377. package/src/views/page-system/menu/modal/icon-modal.tsx +87 -0
  378. package/src/views/page-system/menu/modal/submit-modal.tsx +263 -0
  379. package/src/views/page-system/permission/index.tsx +562 -0
  380. package/src/views/page-system/permission/modal/detail-modal.tsx +179 -0
  381. package/src/views/page-system/permission/modal/submit-modal.less +146 -0
  382. package/src/views/page-system/permission/modal/submit-modal.tsx +650 -0
  383. package/src/views/page-system/role/index.tsx +163 -0
  384. package/src/views/page-system/role/modal/detail-modal.tsx +127 -0
  385. package/src/views/page-system/role/modal/permission-assign-group-rules.ts +86 -0
  386. package/src/views/page-system/role/modal/permission-modal.tsx +111 -0
  387. package/src/views/page-system/role/modal/role-modal-shell-styles.ts +21 -0
  388. package/src/views/page-system/role/modal/role-permission-assign-panel.tsx +916 -0
  389. package/src/views/page-system/role/modal/role-permission-assign-shared.ts +1047 -0
  390. package/src/views/page-system/role/modal/submit-modal.tsx +193 -0
  391. package/src/views/page-system/sys-config/index.tsx +294 -0
  392. package/src/views/page-system/user/components/user-role-column.tsx +87 -0
  393. package/src/views/page-system/user/index.tsx +439 -0
  394. package/src/views/page-system/user/modal/assign-roles-modal.tsx +389 -0
  395. package/src/views/page-system/user/modal/detail-modal.tsx +72 -0
  396. package/src/views/page-system/user/modal/modal-style/submit-modal.less +40 -0
  397. package/src/views/page-system/user/modal/submit-modal.less +40 -0
  398. package/src/views/page-system/user/modal/submit-modal.tsx +287 -0
  399. package/src/views/page-system/user/userFormShared.ts +51 -0
  400. package/tailwind.config.js +17 -0
  401. package/tsconfig.app.json +48 -0
  402. package/tsconfig.json +11 -0
  403. package/tsconfig.node.json +26 -0
  404. package/vite.config.ts +264 -0
@@ -0,0 +1,472 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import clsx from 'clsx';
4
+ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
5
+ import CxSelect from '@/components/cx-ui/select';
6
+
7
+ interface PaginationProps {
8
+ /** Current page (0-indexed by default, see zeroIndexed prop) */
9
+ page: number;
10
+ /** Total number of items */
11
+ total: number;
12
+ /** Items per page */
13
+ pageSize: number;
14
+ /** Callback when page changes */
15
+ onPageChange: (page: number) => void;
16
+ /** Available page size options */
17
+ pageSizeOptions?: number[];
18
+ /** Callback when page size changes */
19
+ onPageSizeChange?: (size: number) => void;
20
+ /** Whether to show stats ("Showing X-Y of Z") */
21
+ showStats?: boolean;
22
+ /** Whether to show page size selector */
23
+ showSizeChanger?: boolean;
24
+ /** Whether to show first/last page buttons */
25
+ showFirstLast?: boolean;
26
+ /** Maximum number of page buttons to show (default: 7) */
27
+ maxPageButtons?: number;
28
+ /** Layout variant */
29
+ variant?: 'spread' | 'compact' | 'simple' | 'slice';
30
+ /** Button size */
31
+ size?: 'sm' | 'md';
32
+ /** Whether page is 0-indexed (default: true) */
33
+ zeroIndexed?: boolean;
34
+ /** Translation key prefix */
35
+ translationPrefix?: string;
36
+ /** Whether pagination is disabled */
37
+ disabled?: boolean;
38
+ /** Additional CSS class */
39
+ className?: string;
40
+ /** For simple variant without total - whether there are more views */
41
+ hasMore?: boolean;
42
+ /** slice variant: whether current page is first */
43
+ isFirst?: boolean;
44
+ /** slice variant: whether current page is last */
45
+ isLast?: boolean;
46
+ }
47
+
48
+ /**
49
+ * 增强版分页组件(与 ui/Pagination 行为一致,供 table-view 等使用)
50
+ * Enhanced Pagination Component with page numbers, ellipsis, and page size selector.
51
+ */
52
+ const Pagination: React.FC<PaginationProps> = ({
53
+ page,
54
+ total = 0,
55
+ pageSize,
56
+ onPageChange,
57
+ pageSizeOptions = [10, 20, 50, 100],
58
+ onPageSizeChange,
59
+ showStats = true,
60
+ showSizeChanger = false,
61
+ showFirstLast = true,
62
+ maxPageButtons = 7,
63
+ variant = 'spread',
64
+ size = 'md',
65
+ zeroIndexed = true,
66
+ translationPrefix = 'common.pagination',
67
+ disabled = false,
68
+ className,
69
+ hasMore = false,
70
+ isFirst,
71
+ isLast,
72
+ }) => {
73
+ const { t } = useTranslation();
74
+ const [navLock, setNavLock] = React.useState(false)
75
+
76
+ const resolvedPageSizeNum = (() => {
77
+ const n = Number(pageSize);
78
+ return Number.isFinite(n) && n > 0 ? n : pageSizeOptions[0];
79
+ })();
80
+
81
+ // Normalize to 0-indexed internally
82
+ const currentPage = zeroIndexed ? page : page - 1;
83
+ const totalPages = Math.max(1, Math.ceil(total / resolvedPageSizeNum));
84
+
85
+ // Calculate displayed range
86
+ const startItem = total > 0 ? currentPage * resolvedPageSizeNum + 1 : 0;
87
+ const endItem = Math.min((currentPage + 1) * resolvedPageSizeNum, total);
88
+
89
+ // Navigation handlers
90
+ const goToPage = (targetPage: number) => {
91
+ if (disabled || navLock) return;
92
+ // slice 模式后端不返回 total,不能用 totalPages 去 clamp,否则“下一页”会永远被限制在第 1 页
93
+ const normalizedTarget =
94
+ variant === 'slice'
95
+ ? Math.max(0, targetPage)
96
+ : Math.max(0, Math.min(totalPages - 1, targetPage));
97
+ setNavLock(true)
98
+ onPageChange(zeroIndexed ? normalizedTarget : normalizedTarget + 1);
99
+ };
100
+
101
+ const handleFirst = () => goToPage(0);
102
+ const handleLast = () => goToPage(totalPages - 1);
103
+ const handlePrev = () => {
104
+ if (variant === 'slice') {
105
+ if (disabled || navLock) return
106
+ if (!isFirst) {
107
+ setNavLock(true)
108
+ onPageChange(zeroIndexed ? Math.max(0, currentPage - 1) : currentPage);
109
+ }
110
+ } else {
111
+ goToPage(currentPage - 1);
112
+ }
113
+ };
114
+ const handleNext = () => {
115
+ if (variant === 'simple') {
116
+ if (hasMore) onPageChange(zeroIndexed ? currentPage + 1 : currentPage + 2);
117
+ } else if (variant === 'slice') {
118
+ if (disabled || navLock) return
119
+ if (!isLast) {
120
+ setNavLock(true)
121
+ onPageChange(zeroIndexed ? currentPage + 1 : currentPage + 2);
122
+ }
123
+ } else {
124
+ goToPage(currentPage + 1);
125
+ }
126
+ };
127
+
128
+ React.useEffect(() => {
129
+ setNavLock(false)
130
+ }, [page, pageSize, disabled, variant])
131
+
132
+ const isPrevDisabled =
133
+ disabled ||
134
+ (variant === 'slice'
135
+ ? !!isFirst
136
+ : currentPage === 0);
137
+ const isNextDisabled =
138
+ disabled ||
139
+ (variant === 'slice'
140
+ ? !!isLast
141
+ : (variant === 'simple' ? !hasMore : currentPage >= totalPages - 1));
142
+
143
+ // Generate page numbers with ellipsis
144
+ const generatePageNumbers = (): (number | 'ellipsis-start' | 'ellipsis-end')[] => {
145
+ if (totalPages <= maxPageButtons) {
146
+ return Array.from({ length: totalPages }, (_, i) => i);
147
+ }
148
+
149
+ const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = [];
150
+ const sideButtons = Math.floor((maxPageButtons - 3) / 2); // buttons on each side of current
151
+
152
+ // Always show first page
153
+ pages.push(0);
154
+
155
+ // Calculate range around current page
156
+ let rangeStart = Math.max(1, currentPage - sideButtons);
157
+ let rangeEnd = Math.min(totalPages - 2, currentPage + sideButtons);
158
+
159
+ // Adjust range if near edges
160
+ if (currentPage <= sideButtons + 1) {
161
+ rangeEnd = Math.min(totalPages - 2, maxPageButtons - 3);
162
+ } else if (currentPage >= totalPages - sideButtons - 2) {
163
+ rangeStart = Math.max(1, totalPages - maxPageButtons + 2);
164
+ }
165
+
166
+ // Add start ellipsis
167
+ if (rangeStart > 1) {
168
+ pages.push('ellipsis-start');
169
+ }
170
+
171
+ // Add middle views
172
+ for (let i = rangeStart; i <= rangeEnd; i++) {
173
+ pages.push(i);
174
+ }
175
+
176
+ // Add end ellipsis
177
+ if (rangeEnd < totalPages - 2) {
178
+ pages.push('ellipsis-end');
179
+ }
180
+
181
+ // Always show last page
182
+ if (totalPages > 1) {
183
+ pages.push(totalPages - 1);
184
+ }
185
+
186
+ return pages;
187
+ };
188
+
189
+ // Styles
190
+ const sizeClasses = {
191
+ sm: 'h-7 min-w-[28px] text-xs',
192
+ md: 'h-8 min-w-[32px] text-sm'
193
+ };
194
+
195
+ const buttonBase = clsx(
196
+ "inline-flex items-center justify-center rounded border transition-all",
197
+ "disabled:opacity-50 disabled:cursor-not-allowed",
198
+ sizeClasses[size]
199
+ );
200
+
201
+ const navButton = clsx(
202
+ buttonBase,
203
+ "px-2 border-gray-300 dark:border-gray-600",
204
+ "text-gray-700 dark:text-gray-300",
205
+ "hover:bg-gray-50 dark:hover:bg-gray-700",
206
+ "active:scale-95 active:bg-gray-100 dark:active:bg-gray-600"
207
+ );
208
+
209
+ const pageButton = (isActive: boolean) => clsx(
210
+ buttonBase,
211
+ "px-2.5",
212
+ isActive
213
+ ? "bg-blue-600 border-blue-600 text-white hover:bg-blue-700"
214
+ : clsx(
215
+ "border-gray-300 dark:border-gray-600",
216
+ "text-gray-700 dark:text-gray-300",
217
+ "hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-blue-300 dark:hover:border-blue-500",
218
+ "active:scale-95"
219
+ )
220
+ );
221
+
222
+ const ellipsisClass = clsx(
223
+ "inline-flex items-center justify-center px-1 text-gray-400 dark:text-gray-500",
224
+ sizeClasses[size]
225
+ );
226
+
227
+ // Simple variant (for FeedbackPage style - no total)
228
+ if (variant === 'simple') {
229
+ return (
230
+ <div className={clsx("flex justify-between items-center pt-8", className)}>
231
+ <button
232
+ onClick={handlePrev}
233
+ disabled={isPrevDisabled}
234
+ className={clsx(navButton, "px-4 py-2 rounded-lg")}
235
+ >
236
+ {t(`${translationPrefix}.previous`, { defaultValue: 'Previous' })}
237
+ </button>
238
+ <span className="text-sm text-gray-500 dark:text-gray-400">
239
+ {t(`${translationPrefix}.page`, { page: currentPage + 1, defaultValue: `Page ${currentPage + 1}` })}
240
+ </span>
241
+ <button
242
+ onClick={handleNext}
243
+ disabled={isNextDisabled}
244
+ className={clsx(navButton, "px-4 py-2 rounded-lg")}
245
+ >
246
+ {t(`${translationPrefix}.next`, { defaultValue: 'Next' })}
247
+ </button>
248
+ </div>
249
+ );
250
+ }
251
+
252
+ // Slice variant(后端切片:首页/上一页/下一页 + 每页条数,同一行靠右)
253
+ if (variant === 'slice') {
254
+ return (
255
+ <div
256
+ className={clsx(
257
+ 'flex flex-wrap items-center justify-end gap-2',
258
+ className,
259
+ )}>
260
+ <button
261
+ onClick={handleFirst}
262
+ disabled={isPrevDisabled}
263
+ className={navButton}
264
+ title={t(`${translationPrefix}.first`, { defaultValue: 'First' })}
265
+ >
266
+ <ChevronsLeft size={size === 'sm' ? 14 : 16} />
267
+ </button>
268
+ <button
269
+ onClick={handlePrev}
270
+ disabled={isPrevDisabled}
271
+ className={navButton}
272
+ title={t(`${translationPrefix}.previous`, { defaultValue: 'Previous' })}
273
+ >
274
+ <ChevronLeft size={size === 'sm' ? 14 : 16} />
275
+ </button>
276
+ <span className="px-2 text-sm text-gray-500 dark:text-gray-400">
277
+ {t(`${translationPrefix}.page`, {
278
+ page: currentPage + 1,
279
+ defaultValue: `Page ${currentPage + 1}`,
280
+ })}
281
+ </span>
282
+ <button
283
+ onClick={handleNext}
284
+ disabled={isNextDisabled}
285
+ className={navButton}
286
+ title={t(`${translationPrefix}.next`, { defaultValue: 'Next' })}
287
+ >
288
+ <ChevronRight size={size === 'sm' ? 14 : 16} />
289
+ </button>
290
+ {showSizeChanger && onPageSizeChange ? (
291
+ <CxSelect.Medium
292
+ size="small"
293
+ value={String(resolvedPageSizeNum)}
294
+ onChange={(val) => {
295
+ const n = Number(val);
296
+ if (!Number.isFinite(n)) return;
297
+ onPageSizeChange(n);
298
+ }}
299
+ disabled={disabled}
300
+ options={pageSizeOptions.map((opt) => ({
301
+ label: `${opt} ${t(`${translationPrefix}.per_page`, { defaultValue: '/ page' })}`,
302
+ value: String(opt),
303
+ }))}
304
+ style={{ width: 120 }}
305
+ getPopupContainer={() => document.body}
306
+ />
307
+ ) : null}
308
+ </div>
309
+ );
310
+ }
311
+
312
+ // Compact variant (centered, no stats)
313
+ if (variant === 'compact') {
314
+ const pageNumbers = generatePageNumbers();
315
+ return (
316
+ <div className={clsx("flex justify-center items-center gap-1", className)}>
317
+ {showFirstLast && (
318
+ <button onClick={handleFirst} disabled={isPrevDisabled} className={navButton} title={t(`${translationPrefix}.first`, { defaultValue: 'First' })}>
319
+ <ChevronsLeft size={size === 'sm' ? 14 : 16} />
320
+ </button>
321
+ )}
322
+ <button onClick={handlePrev} disabled={isPrevDisabled} className={navButton}>
323
+ <ChevronLeft size={size === 'sm' ? 14 : 16} />
324
+ </button>
325
+
326
+ {pageNumbers.map((p, idx) =>
327
+ typeof p === 'number' ? (
328
+ <button
329
+ key={p}
330
+ onClick={() => goToPage(p)}
331
+ disabled={disabled}
332
+ className={pageButton(p === currentPage)}
333
+ >
334
+ {p + 1}
335
+ </button>
336
+ ) : (
337
+ <span key={`${p}-${idx}`} className={ellipsisClass}>...</span>
338
+ )
339
+ )}
340
+
341
+ <button onClick={handleNext} disabled={isNextDisabled} className={navButton}>
342
+ <ChevronRight size={size === 'sm' ? 14 : 16} />
343
+ </button>
344
+ {showFirstLast && (
345
+ <button onClick={handleLast} disabled={isNextDisabled} className={navButton} title={t(`${translationPrefix}.last`, { defaultValue: 'Last' })}>
346
+ <ChevronsRight size={size === 'sm' ? 14 : 16} />
347
+ </button>
348
+ )}
349
+ </div>
350
+ );
351
+ }
352
+
353
+ // Default: spread variant (full featured)
354
+ const pageNumbers = generatePageNumbers();
355
+
356
+ return (
357
+ <div className={clsx(
358
+ "flex flex-wrap items-center justify-between gap-4",
359
+ className
360
+ )}>
361
+ {/* Left: Stats */}
362
+ <div className="flex items-center gap-4">
363
+ {showStats && (
364
+ <span className="text-sm text-gray-500 dark:text-gray-400">
365
+ {t(`${translationPrefix}.showing`, {
366
+ start: startItem,
367
+ end: endItem,
368
+ total: total,
369
+ defaultValue: `Showing ${startItem}-${endItem} of ${total}`
370
+ })}
371
+ </span>
372
+ )}
373
+
374
+ {/* Page Size Selector */}
375
+ {showSizeChanger && onPageSizeChange && (
376
+ <div className="flex items-center gap-2">
377
+ <CxSelect.Medium
378
+ size="small"
379
+ /** antd Select 受控值与 options.value 类型须一致;统一用 string 避免 number/string 混用不触发变更 */
380
+ value={String(resolvedPageSizeNum)}
381
+ onChange={(val) => {
382
+ const n = Number(val);
383
+ if (!Number.isFinite(n)) return;
384
+ onPageSizeChange(n);
385
+ /**
386
+ * 勿在此处再调 goToPage(0)。
387
+ * 父级若在 onPageChange 里写 handleTableChange({ current, pageSize: paginationData.pageSize }),
388
+ * 会与本次 setState 同 tick 触发,闭包里的 pageSize 仍是旧值,会把刚改的条数覆盖回去。
389
+ * 需要回到第一页时:在 onPageSizeChange 里与条数一并处理,或交给 table-view 的 handleTableChange(已按条数变化重置 page)。
390
+ */
391
+ }}
392
+ disabled={disabled}
393
+ options={pageSizeOptions.map((opt) => ({
394
+ label: `${opt} ${t(`${translationPrefix}.per_page`, { defaultValue: '/ page' })}`,
395
+ value: String(opt),
396
+ }))}
397
+ style={{ width: 120 }}
398
+ getPopupContainer={() => document.body}
399
+ />
400
+ </div>
401
+ )}
402
+ </div>
403
+
404
+ {/* Right: Page Navigation */}
405
+ <div className="flex items-center gap-1">
406
+ {/* First */}
407
+ {showFirstLast && (
408
+ <button
409
+ onClick={handleFirst}
410
+ disabled={isPrevDisabled}
411
+ className={navButton}
412
+ title={t(`${translationPrefix}.first`, { defaultValue: 'First page' })}
413
+ >
414
+ <ChevronsLeft size={size === 'sm' ? 14 : 16} />
415
+ </button>
416
+ )}
417
+
418
+ {/* Previous */}
419
+ <button
420
+ onClick={handlePrev}
421
+ disabled={isPrevDisabled}
422
+ className={navButton}
423
+ title={t(`${translationPrefix}.previous`, { defaultValue: 'Previous' })}
424
+ >
425
+ <ChevronLeft size={size === 'sm' ? 14 : 16} />
426
+ </button>
427
+
428
+ {/* Page Numbers */}
429
+ <div className="flex items-center gap-1 mx-1">
430
+ {pageNumbers.map((p, idx) =>
431
+ typeof p === 'number' ? (
432
+ <button
433
+ key={p}
434
+ onClick={() => goToPage(p)}
435
+ disabled={disabled}
436
+ className={pageButton(p === currentPage)}
437
+ >
438
+ {p + 1}
439
+ </button>
440
+ ) : (
441
+ <span key={`${p}-${idx}`} className={ellipsisClass}>...</span>
442
+ )
443
+ )}
444
+ </div>
445
+
446
+ {/* Next */}
447
+ <button
448
+ onClick={handleNext}
449
+ disabled={isNextDisabled}
450
+ className={navButton}
451
+ title={t(`${translationPrefix}.next`, { defaultValue: 'Next' })}
452
+ >
453
+ <ChevronRight size={size === 'sm' ? 14 : 16} />
454
+ </button>
455
+
456
+ {/* Last */}
457
+ {showFirstLast && (
458
+ <button
459
+ onClick={handleLast}
460
+ disabled={isNextDisabled}
461
+ className={navButton}
462
+ title={t(`${translationPrefix}.last`, { defaultValue: 'Last page' })}
463
+ >
464
+ <ChevronsRight size={size === 'sm' ? 14 : 16} />
465
+ </button>
466
+ )}
467
+ </div>
468
+ </div>
469
+ );
470
+ };
471
+
472
+ export default Pagination;
@@ -0,0 +1,175 @@
1
+ import { CxTag } from '@cx-ui'
2
+ import clsx from 'clsx'
3
+ import { Copy } from 'lucide-react'
4
+ import { Fragment, memo, useState } from 'react'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { useCopyToClipboard } from '@/hooks/useCopyToClipboard'
7
+
8
+ /**
9
+ * 接口路径按 `/` 分段,每段一个 CxTag,颜色按层级循环;前导 `/` 单独展示。
10
+ * 默认不用 `pill`,与列表「状态」等胶囊 Tag 区分。
11
+ * 左侧复制按钮复制完整路径(自动补全前导 `/`)。
12
+ */
13
+
14
+ const SEGMENT_COLORS = [
15
+ '#2563eb',
16
+ '#0d9488',
17
+ '#16a34a',
18
+ '#ca8a04',
19
+ '#9333ea',
20
+ '#dc2626',
21
+ ]
22
+
23
+ export type PathTagsProps = {
24
+ path?: string | null
25
+ /** 无有效分段时展示 */
26
+ emptyText?: string
27
+ className?: string
28
+ /** 是否使用胶囊 Tag;不传时 `whole` 默认 true,`segment` 默认 false */
29
+ pill?: boolean
30
+ /** 是否紧凑模式:`segment` 默认 true;`whole` 默认 false */
31
+ compact?: boolean
32
+ /** `renderMode="whole"` 时的 Tag 颜色(如与 httpMethod 同色) */
33
+ color?: string
34
+ /**
35
+ * 渲染模式:
36
+ * - whole(默认):整条路径一个 Tag
37
+ * - segment:按 `/` 分段多个 Tag(历史效果)
38
+ */
39
+ renderMode?: string
40
+ /**
41
+ * `renderMode="segment"` 时的颜色模式:
42
+ * - same:所有分段同色(直接用 CxTag 默认样式,便于后续统一调整/一键切回)
43
+ * - segment:按层级循环配色(历史效果)
44
+ */
45
+ colorMode?: string
46
+ }
47
+
48
+ /** 用于展示与复制:无内容返回空串;否则保证以 `/` 开头 */
49
+ function normalizePathForCopy(raw: string): string {
50
+ const s = String(raw).trim()
51
+ if (!s) return ''
52
+ return s.startsWith('/') ? s : `/${s}`
53
+ }
54
+
55
+ function splitSegments(normalized: string): string[] {
56
+ if (!normalized) return []
57
+ return normalized.split('/').filter((p) => p.length > 0)
58
+ }
59
+
60
+ function PathTagsInner({
61
+ path,
62
+ emptyText = '-',
63
+ className,
64
+ color,
65
+ renderMode = 'whole',
66
+ colorMode = 'same',
67
+ pill,
68
+ compact,
69
+ }: PathTagsProps) {
70
+ const { t } = useTranslation()
71
+ const [showCopy, setShowCopy] = useState(false)
72
+ const { copy } = useCopyToClipboard()
73
+
74
+ const compactResolved = compact ?? (renderMode === 'segment')
75
+ const pillResolved = pill ?? (renderMode === 'whole')
76
+
77
+ // 用于显示的标准化路径(保证以 / 开头)
78
+ const fullPath =
79
+ path != null && String(path).trim() !== ''
80
+ ? normalizePathForCopy(String(path))
81
+ : ''
82
+
83
+ // 用于复制的原始路径(只 trim,不添加前导 /)
84
+ const rawPathForCopy =
85
+ path != null && String(path).trim() !== ''
86
+ ? String(path).trim()
87
+ : ''
88
+
89
+ const segments = fullPath ? splitSegments(fullPath) : []
90
+
91
+ if (!fullPath || segments.length === 0) {
92
+ return (
93
+ <span className="text-gray-500 dark:text-gray-400">{emptyText}</span>
94
+ )
95
+ }
96
+
97
+ return (
98
+ <div
99
+ className={clsx(
100
+ 'inline-flex max-w-full flex-wrap items-center gap-x-1 gap-y-1',
101
+ compactResolved ? 'gap-x-0.5' : 'gap-x-1',
102
+ className,
103
+ )}
104
+ onMouseEnter={() => setShowCopy(true)}
105
+ onMouseLeave={() => setShowCopy(false)}>
106
+ <div className="inline-flex min-w-0 flex-wrap items-center gap-y-1">
107
+ {renderMode === 'segment' ? (
108
+ <>
109
+ <span
110
+ className={clsx(
111
+ 'select-none self-center text-gray-400 dark:text-gray-500',
112
+ compactResolved ? 'mx-0' : 'mx-0.5',
113
+ )}
114
+ aria-hidden>
115
+ /
116
+ </span>
117
+ {segments.map((seg, i) => (
118
+ <Fragment key={`${i}-${seg}`}>
119
+ {i > 0 ? (
120
+ <span
121
+ className={clsx(
122
+ 'select-none self-center text-gray-400 dark:text-gray-500',
123
+ compactResolved ? 'mx-0' : 'mx-0.5',
124
+ )}
125
+ aria-hidden>
126
+ /
127
+ </span>
128
+ ) : null}
129
+ <CxTag
130
+ color={
131
+ colorMode === 'segment'
132
+ ? SEGMENT_COLORS[i % SEGMENT_COLORS.length]
133
+ : undefined
134
+ }
135
+ pill={pillResolved}
136
+ className={compactResolved ? 'px-1 py-0 text-xs' : undefined}>
137
+ {seg}
138
+ </CxTag>
139
+ </Fragment>
140
+ ))}
141
+ </>
142
+ ) : (
143
+ <CxTag
144
+ color={color}
145
+ pill={pillResolved}
146
+ className={compactResolved ? 'px-1 py-0 text-xs' : undefined}
147
+ >
148
+ {fullPath}
149
+ </CxTag>
150
+ )}
151
+ </div>
152
+ <button
153
+ type="button"
154
+ className={clsx(
155
+ 'inline-flex rounded p-0.5 text-gray-500 transition-all',
156
+ 'hover:bg-gray-100 hover:text-blue-600',
157
+ 'dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-blue-400',
158
+ compactResolved ? 'ml-0.5' : 'ml-1',
159
+ !showCopy && 'opacity-0 pointer-events-none',
160
+ )}
161
+ title={t('common.copy_path')}
162
+ aria-label={t('common.copy_path')}
163
+ onClick={(e) => {
164
+ e.stopPropagation()
165
+ void copy(rawPathForCopy)
166
+ }}>
167
+ <Copy size={14} strokeWidth={2} aria-hidden />
168
+ </button>
169
+ </div>
170
+ )
171
+ }
172
+
173
+ PathTagsInner.displayName = 'PathTags'
174
+
175
+ export const PathTags = memo(PathTagsInner)
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ import clsx from 'clsx'
3
+ import { useSystemConfig } from '@/config/system'
4
+
5
+ type SystemLogoMarkProps = {
6
+ className?: string
7
+ /** 没有系统 Logo 时的占位形态:square=方形,circle=圆形 */
8
+ fallbackShape?: 'circle' | 'square'
9
+ }
10
+
11
+ /** 系统 Logo 标识:统一登录/注册/会话首页的品牌展示(有配置用配置)。 */
12
+ export function SystemLogoMark({
13
+ className,
14
+ fallbackShape = 'square',
15
+ }: SystemLogoMarkProps) {
16
+ const { systemLogo, systemName } = useSystemConfig()
17
+ const shapeCls =
18
+ fallbackShape === 'circle' ? 'rounded-full' : 'rounded-xl'
19
+
20
+ if (systemLogo) {
21
+ return (
22
+ <img
23
+ src={systemLogo}
24
+ alt={systemName}
25
+ className={clsx(shapeCls, 'object-cover', className)}
26
+ onError={(e) => {
27
+ e.currentTarget.style.display = 'none'
28
+ }}
29
+ />
30
+ )
31
+ }
32
+
33
+ return (
34
+ <div
35
+ className={clsx(
36
+ shapeCls,
37
+ 'bg-gradient-to-tr from-blue-600 to-purple-600 flex items-center justify-center shadow-lg shadow-blue-500/20',
38
+ className,
39
+ )}
40
+ aria-hidden
41
+ >
42
+ <span className="text-white font-bold">
43
+ {(systemName || 'A').substring(0, 2).toUpperCase()}
44
+ </span>
45
+ </div>
46
+ )
47
+ }
48
+