@_tc/template-core 0.0.1-bate.37 → 0.0.1-bate.39

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 (164) hide show
  1. package/cjs/bundler/utils.js +13 -5
  2. package/cjs/packages/core/index.js +1 -1
  3. package/cjs/packages/core/loader/config.js +4 -2
  4. package/cjs/packages/core/loader/controller.js +2 -3
  5. package/cjs/packages/core/loader/extend.js +1 -2
  6. package/cjs/packages/core/loader/middleware.js +2 -3
  7. package/cjs/packages/core/loader/model.js +7 -3
  8. package/cjs/packages/core/loader/router-schema.js +4 -2
  9. package/cjs/packages/core/loader/router.js +10 -6
  10. package/cjs/packages/core/loader/service.js +2 -2
  11. package/cjs/packages/utils/runFileFn.js +46 -1
  12. package/esm/bundler/utils.js +13 -5
  13. package/esm/packages/core/index.js +1 -2
  14. package/esm/packages/core/loader/config.js +4 -3
  15. package/esm/packages/core/loader/controller.js +2 -4
  16. package/esm/packages/core/loader/extend.js +1 -3
  17. package/esm/packages/core/loader/middleware.js +2 -4
  18. package/esm/packages/core/loader/model.js +7 -4
  19. package/esm/packages/core/loader/router-schema.js +4 -3
  20. package/esm/packages/core/loader/router.js +10 -7
  21. package/esm/packages/core/loader/service.js +2 -3
  22. package/esm/packages/utils/runFileFn.js +47 -1
  23. package/fe/frontend/dash/Dashboard.js +11 -1
  24. package/fe/frontend/dash/dash.entry.js +19 -1
  25. package/fe/frontend/main.js +2 -0
  26. package/fe/frontend/widgets/common/CRUD/CRUD.js +1 -0
  27. package/fe/frontend/widgets/common/importComponent.js +1 -0
  28. package/fe/frontend/widgets/common/language.js +1 -0
  29. package/fe/frontend/widgets/common/menu.d.ts +12 -0
  30. package/fe/frontend/widgets/common/menu.js +21 -0
  31. package/fe/frontend/widgets/common/request.d.ts +3 -0
  32. package/fe/frontend/widgets/common/request.js +6 -0
  33. package/fe/frontend/widgets/components/BasePage/HeaderView.js +3 -1
  34. package/fe/frontend/widgets/defaultPages/Schema/components/CallCom/PopFrom.js +15 -0
  35. package/fe/frontend/widgets/defaultPages/Schema/components/SchemaSearch/index.js +5 -0
  36. package/fe/frontend/widgets/defaultPages/Schema/components/SchemaTable/index.js +13 -1
  37. package/fe/frontend/widgets/defaultPages/Schema/data/eventInfo.js +4 -4
  38. package/fe/frontend/widgets/defaultPages/Schema/index.js +7 -0
  39. package/fe/frontend/widgets/defaultPages/Schema/schemaType.d.ts +9 -0
  40. package/fe/frontend/widgets/defaultPages/Schema/stores/schemaEventBus.d.ts +19 -0
  41. package/fe/frontend/widgets/defaultPages/Schema/stores/schemaEventBus.js +4 -0
  42. package/fe/frontend/widgets/defaultPages/Schema/stores/schemaStore.d.ts +14 -0
  43. package/fe/frontend/widgets/defaultPages/Schema/stores/schemaStore.js +2 -0
  44. package/fe/frontend/widgets/defaultPages/Schema/utils/schemaConversion.js +43 -0
  45. package/fe/frontend/widgets/defaultPages/Schema/utils/validator.d.ts +3 -0
  46. package/fe/frontend/widgets/defaultPages/Schema/utils/validator.js +5 -0
  47. package/fe/frontend/widgets/defaultPages/SidebarSlotPage/SidebarSlotContainer.js +7 -0
  48. package/fe/frontend/widgets/defaultPages/SidebarSlotPage/index.js +1 -0
  49. package/fe/frontend/widgets/defaultPages/SlotPage/index.js +2 -0
  50. package/fe/frontend/widgets/hooks/useCurrentMenuData.d.ts +8 -0
  51. package/fe/frontend/widgets/hooks/useCurrentMenuData.js +8 -0
  52. package/fe/frontend/widgets/store/mode.d.ts +3 -0
  53. package/fe/frontend/widgets/store/mode.js +1 -0
  54. package/fe/model/types/data/button.d.ts +9 -0
  55. package/fe/model/types/data/button.js +15 -0
  56. package/fe/model/types/data/component.d.ts +24 -0
  57. package/fe/model/types/data/component.js +10 -0
  58. package/fe/model/types/data/fetchInfo.d.ts +12 -0
  59. package/fe/model/types/data/schema.d.ts +51 -0
  60. package/fe/model/types/menuType.d.ts +29 -0
  61. package/fe/model/types/model.d.ts +13 -0
  62. package/fe/packages/common/i18n/default.d.ts +5 -0
  63. package/fe/packages/common/i18n/default.js +5 -0
  64. package/fe/packages/common/i18n/en-US.d.ts +5 -0
  65. package/fe/packages/common/i18n/en-US.js +5 -0
  66. package/fe/packages/common/i18n/index.d.ts +24 -0
  67. package/fe/packages/common/i18n/index.js +31 -0
  68. package/fe/packages/common/i18n/types.d.ts +24 -0
  69. package/fe/packages/ui/react/components/Button/Button.d.ts +33 -0
  70. package/fe/packages/ui/react/components/Button/Button.js +3 -0
  71. package/fe/packages/ui/react/components/Button/SumbitButton.d.ts +4 -0
  72. package/fe/packages/ui/react/components/Button/SumbitButton.js +4 -0
  73. package/fe/packages/ui/react/components/ConfirmDialog/ConfirmDialog.d.ts +8 -0
  74. package/fe/packages/ui/react/components/DataTable/index.d.ts +19 -0
  75. package/fe/packages/ui/react/components/DataTable/index.js +4 -0
  76. package/fe/packages/ui/react/components/Date/Calendar.d.ts +13 -0
  77. package/fe/packages/ui/react/components/Date/Calendar.js +10 -1
  78. package/fe/packages/ui/react/components/Date/Date.d.ts +11 -0
  79. package/fe/packages/ui/react/components/Date/Date.js +19 -0
  80. package/fe/packages/ui/react/components/Date/LocaleContext.d.ts +4 -0
  81. package/fe/packages/ui/react/components/Date/LocaleContext.js +4 -0
  82. package/fe/packages/ui/react/components/Date/LocaleProvider.d.ts +11 -0
  83. package/fe/packages/ui/react/components/Date/LocaleProvider.js +11 -0
  84. package/fe/packages/ui/react/components/Date/TimePicker.js +1 -0
  85. package/fe/packages/ui/react/components/Date/dateLocaleStore.d.ts +6 -0
  86. package/fe/packages/ui/react/components/Date/locales.d.ts +19 -0
  87. package/fe/packages/ui/react/components/Date/locales.js +9 -0
  88. package/fe/packages/ui/react/components/Drawer/Drawer.d.ts +6 -0
  89. package/fe/packages/ui/react/components/Dropdown/Dropdown.d.ts +1 -0
  90. package/fe/packages/ui/react/components/Form/Form.d.ts +6 -0
  91. package/fe/packages/ui/react/components/Form/FormItem.d.ts +21 -0
  92. package/fe/packages/ui/react/components/Form/FormItem.js +8 -1
  93. package/fe/packages/ui/react/components/Form/SchemaForm/data.js +1 -0
  94. package/fe/packages/ui/react/components/Form/SchemaForm/index.d.ts +93 -0
  95. package/fe/packages/ui/react/components/Form/SchemaForm/index.js +5 -1
  96. package/fe/packages/ui/react/components/ImagePreview/ImagePreview.js +8 -0
  97. package/fe/packages/ui/react/components/ImagePreview/PreviewImage.d.ts +3 -0
  98. package/fe/packages/ui/react/components/Input/Input.d.ts +22 -0
  99. package/fe/packages/ui/react/components/Input/Input.js +3 -0
  100. package/fe/packages/ui/react/components/InputNumber/InputNumber.d.ts +2 -0
  101. package/fe/packages/ui/react/components/Label/Label.d.ts +29 -0
  102. package/fe/packages/ui/react/components/Label/Label.js +2 -0
  103. package/fe/packages/ui/react/components/Menu/Menu.js +4 -0
  104. package/fe/packages/ui/react/components/Menu/SubMenu.d.ts +7 -0
  105. package/fe/packages/ui/react/components/Menu/SubMenu.js +46 -1
  106. package/fe/packages/ui/react/components/Menu/menuTypes.d.ts +1 -0
  107. package/fe/packages/ui/react/components/Message/Message.d.ts +7 -0
  108. package/fe/packages/ui/react/components/Message/Message.js +3 -0
  109. package/fe/packages/ui/react/components/Message/MessageManager.js +8 -0
  110. package/fe/packages/ui/react/components/Modal/Modal.d.ts +6 -0
  111. package/fe/packages/ui/react/components/Modal/Modal.js +1 -0
  112. package/fe/packages/ui/react/components/Modal/ModalManager.d.ts +12 -0
  113. package/fe/packages/ui/react/components/Modal/ModalManager.js +4 -1
  114. package/fe/packages/ui/react/components/Overlay/Overlay.d.ts +3 -0
  115. package/fe/packages/ui/react/components/Pagination/Pagination.d.ts +7 -0
  116. package/fe/packages/ui/react/components/Pagination/Pagination.js +8 -1
  117. package/fe/packages/ui/react/components/Popup/Popup.js +14 -2
  118. package/fe/packages/ui/react/components/Search/Search.d.ts +3 -0
  119. package/fe/packages/ui/react/components/Select/Select.d.ts +5 -0
  120. package/fe/packages/ui/react/components/Select/Select.js +4 -0
  121. package/fe/packages/ui/react/components/Skeleton/Skeleton.d.ts +15 -0
  122. package/fe/packages/ui/react/components/Skeleton/Skeleton.js +1 -1
  123. package/fe/packages/ui/react/components/TableSearch/TableSearch.d.ts +37 -0
  124. package/fe/packages/ui/react/components/TableSearch/TableSearch.js +4 -1
  125. package/fe/packages/ui/react/components/Textarea/Textarea.d.ts +46 -0
  126. package/fe/packages/ui/react/components/Textarea/Textarea.js +1 -0
  127. package/fe/packages/ui/react/components/Tooltip/Tooltip.d.ts +16 -0
  128. package/fe/packages/ui/react/components/Tooltip/Tooltip.js +8 -0
  129. package/fe/packages/ui/react/components/TreeSelect/TreeSelect.d.ts +6 -0
  130. package/fe/packages/ui/react/components/TreeSelect/TreeSelect.js +6 -0
  131. package/fe/packages/ui/react/components/Upload/Upload.d.ts +27 -0
  132. package/fe/packages/ui/react/components/breadcrumb/breadcrumb.js +9 -0
  133. package/fe/packages/ui/react/components/hooks/useDropdownPositioning.d.ts +6 -0
  134. package/fe/packages/ui/react/components/hooks/useDropdownPositioning.js +14 -0
  135. package/fe/packages/ui/react/components/hooks/useInputController.d.ts +3 -0
  136. package/fe/packages/ui/react/components/hooks/useInputController.js +7 -0
  137. package/fe/packages/ui/react/components/testPage/MenuTestPage.js +3 -0
  138. package/fe/packages/ui/react/components/testPage/index.js +26 -0
  139. package/fe/packages/ui/react/hooks/useExecuteOnce.d.ts +19 -1
  140. package/fe/packages/ui/react/hooks/useExecuteOnce.js +22 -1
  141. package/fe/packages/ui/react/hooks/useRefState.d.ts +12 -0
  142. package/fe/packages/ui/react/hooks/useRefState.js +1 -0
  143. package/fe/packages/ui/react/hooks/useWatch.d.ts +8 -0
  144. package/fe/packages/ui/react/i18n/I18nProvider.d.ts +18 -0
  145. package/fe/packages/ui/react/i18n/useI18n.d.ts +4 -0
  146. package/fe/packages/ui/react/i18n/useI18n.js +4 -0
  147. package/fe/packages/ui/react/index.js +2 -0
  148. package/fe/packages/ui/react/lib/export.d.ts +44 -0
  149. package/fe/packages/ui/react/lib/export.js +40 -0
  150. package/fe/packages/ui/react/lib/utils.d.ts +24 -0
  151. package/fe/packages/ui/react/lib/utils.js +25 -0
  152. package/fe/packages/ui/react/stores/breadcrumb.js +2 -0
  153. package/model/index.d.ts +2 -0
  154. package/model/types/data/button.d.ts +32 -0
  155. package/model/types/data/component.d.ts +61 -0
  156. package/model/types/data/fetchInfo.d.ts +20 -0
  157. package/model/types/data/schema.d.ts +98 -0
  158. package/model/types/data/search.d.ts +7 -0
  159. package/model/types/index.d.ts +2 -0
  160. package/model/types/menuType.d.ts +73 -0
  161. package/model/types/model.d.ts +33 -0
  162. package/model/types/test.d.ts +2 -0
  163. package/package.json +10 -7
  164. package/types/packages/utils/runFileFn.d.ts +5 -3
@@ -1,10 +1,17 @@
1
1
  export interface PaginationProps {
2
+ /** 当前页码 */
2
3
  current: number;
4
+ /** 每页显示数量 */
3
5
  pageSize: number;
6
+ /** 总数据量 */
4
7
  total: number;
8
+ /** 页码改变回调 */
5
9
  onChange: (page: number) => void;
10
+ /** 每页显示数量改变回调 */
6
11
  onPageSizeChange?: (pageSize: number) => void;
12
+ /** 每页显示数量选项 */
7
13
  pageSizeOptions?: number[];
14
+ /** 自定义类名 */
8
15
  className?: string;
9
16
  }
10
17
  export declare function Pagination({ current, pageSize, total, onChange, onPageSizeChange, pageSizeOptions, className, }: PaginationProps): import("react/jsx-runtime").JSX.Element | null;
@@ -5,16 +5,20 @@ import { Button } from '../Button';
5
5
  import { Select } from '../Select';
6
6
  export function Pagination({ current, pageSize, total, onChange, onPageSizeChange, pageSizeOptions = [10, 20, 50, 100], className, }) {
7
7
  const totalPages = Math.ceil(total / pageSize);
8
+ // 生成页码数组
8
9
  const getPageNumbers = () => {
9
10
  const pages = [];
10
- const maxVisible = 7;
11
+ const maxVisible = 7; // 最多显示7个页码
11
12
  if (totalPages <= maxVisible) {
13
+ // 总页数少于等于7,显示所有页码
12
14
  for (let i = 1; i <= totalPages; i++) {
13
15
  pages.push(i);
14
16
  }
15
17
  }
16
18
  else {
19
+ // 总页数大于7,需要省略
17
20
  if (current <= 4) {
21
+ // 当前页在前面
18
22
  for (let i = 1; i <= 5; i++) {
19
23
  pages.push(i);
20
24
  }
@@ -22,6 +26,7 @@ export function Pagination({ current, pageSize, total, onChange, onPageSizeChang
22
26
  pages.push(totalPages);
23
27
  }
24
28
  else if (current >= totalPages - 3) {
29
+ // 当前页在后面
25
30
  pages.push(1);
26
31
  pages.push('...');
27
32
  for (let i = totalPages - 4; i <= totalPages; i++) {
@@ -29,6 +34,7 @@ export function Pagination({ current, pageSize, total, onChange, onPageSizeChang
29
34
  }
30
35
  }
31
36
  else {
37
+ // 当前页在中间
32
38
  pages.push(1);
33
39
  pages.push('...');
34
40
  for (let i = current - 1; i <= current + 1; i++) {
@@ -58,6 +64,7 @@ export function Pagination({ current, pageSize, total, onChange, onPageSizeChang
58
64
  const handlePageSizeChange = (newPageSize) => {
59
65
  if (newPageSize !== undefined && typeof newPageSize === 'number' && newPageSize !== pageSize && onPageSizeChange) {
60
66
  onPageSizeChange(newPageSize);
67
+ // 当改变 pageSize 时,通常需要重置到第一页
61
68
  if (current !== 1) {
62
69
  onChange(1);
63
70
  }
@@ -6,9 +6,11 @@ import { getDefaultDropdownPosition, useDropdownPositioning, } from "../hooks/us
6
6
  const DEFAULT_POPUP_CLASSNAME = cn("fixed z-[9999]", "bg-background rounded-lg shadow-lg border border-gray-200", "animate-in fade-in-0 zoom-in-95");
7
7
  const canUseDocument = () => typeof document !== "undefined";
8
8
  export function Popup({ open, anchorRef, children, className, style, placement = "bottom-end", offset = 8, keepMounted = false, closeOnClickOutside = true, closeOnEscape = true, matchAnchorWidth = false, position, positioning, strategy = getDefaultDropdownPosition, contentRef, container, positionMode = "fixed", hoverBridge = 0, hoverBridgePlacement = "auto", onOpenChange, }) {
9
+ // 默认自己保存弹窗 DOM;如果外部传了 contentRef,就用外部的。
9
10
  const internalContentRef = useRef(null);
10
11
  const panelRef = contentRef ?? internalContentRef;
11
12
  const containerRef = useMemo(() => ({ current: container ?? null }), [container]);
13
+ // 默认用 useDropdownPositioning 算位置;也允许外部直接传 position 或 positioning 接管。
12
14
  const internalPositioning = useDropdownPositioning({
13
15
  open,
14
16
  anchorRef,
@@ -26,6 +28,7 @@ export function Popup({ open, anchorRef, children, className, style, placement =
26
28
  useEffect(() => {
27
29
  if (!open || !closeOnClickOutside)
28
30
  return;
31
+ // 点到触发元素和弹窗外面,就通知外部关闭。
29
32
  const handleClickOutside = (event) => {
30
33
  const target = event.target;
31
34
  const clickedAnchor = anchorRef.current?.contains(target);
@@ -40,6 +43,7 @@ export function Popup({ open, anchorRef, children, className, style, placement =
40
43
  useEffect(() => {
41
44
  if (!open || !closeOnEscape)
42
45
  return;
46
+ // 按 Esc 时关闭,常见弹窗交互都需要这个。
43
47
  const handleKeyDown = (event) => {
44
48
  if (event.key === "Escape") {
45
49
  onOpenChange?.(false);
@@ -49,21 +53,29 @@ export function Popup({ open, anchorRef, children, className, style, placement =
49
53
  return () => document.removeEventListener("keydown", handleKeyDown);
50
54
  }, [closeOnEscape, onOpenChange, open]);
51
55
  const mergedStyle = useMemo(() => ({
56
+ // 定位样式优先来自 hook,也可以被外部 position/style 覆盖。
52
57
  ...resolvedPositioning?.position,
53
58
  ...position,
54
59
  ...style,
55
60
  }), [position, resolvedPositioning?.position, style]);
61
+ // hoverBridge 是透明桥。鼠标从触发元素移动到弹窗时,中间有间距也不会断 hover。
56
62
  const showHorizontalHoverBridge = hoverBridge > 0 &&
57
63
  (placement.startsWith("right") || placement.startsWith("left"));
58
64
  const showBothHorizontalHoverBridges = showHorizontalHoverBridge && hoverBridgePlacement === "horizontal";
59
65
  if (!keepMounted && !open) {
66
+ // 不需要常驻 DOM 时,关闭就不渲染。
60
67
  return null;
61
68
  }
62
69
  const portalContainer = container ?? (canUseDocument() ? document.body : undefined);
63
70
  if (!portalContainer) {
64
71
  return null;
65
72
  }
66
- return createPortal(_jsxs("div", { ref: panelRef, className: cn("tc-ui-popup", DEFAULT_POPUP_CLASSNAME, positionMode === "absolute" && "absolute", !resolvedPositioning?.ready && "invisible", keepMounted && !open && "hidden", className), style: mergedStyle, children: [hoverBridge > 0 && (_jsx("div", { "aria-hidden": "true", className: cn("absolute bg-transparent", placement.startsWith("bottom") && "left-0 right-0 top-0 -translate-y-full", placement.startsWith("top") && "bottom-0 left-0 right-0 translate-y-full", placement.startsWith("right") && "bottom-0 left-0 top-0 -translate-x-full", placement.startsWith("left") && "bottom-0 right-0 top-0 translate-x-full"), style: placement.startsWith("right") || placement.startsWith("left")
73
+ // portal 把弹窗挂到指定容器;没传 container 就挂到 body。
74
+ return createPortal(_jsxs("div", { ref: panelRef, className: cn("tc-ui-popup", DEFAULT_POPUP_CLASSNAME, positionMode === "absolute" && "absolute", !resolvedPositioning?.ready && "invisible", keepMounted && !open && "hidden", className), style: mergedStyle, children: [hoverBridge > 0 && (
75
+ // 默认只补当前 placement 方向上的透明桥。
76
+ _jsx("div", { "aria-hidden": "true", className: cn("absolute bg-transparent", placement.startsWith("bottom") && "left-0 right-0 top-0 -translate-y-full", placement.startsWith("top") && "bottom-0 left-0 right-0 translate-y-full", placement.startsWith("right") && "bottom-0 left-0 top-0 -translate-x-full", placement.startsWith("left") && "bottom-0 right-0 top-0 translate-x-full"), style: placement.startsWith("right") || placement.startsWith("left")
67
77
  ? { width: hoverBridge }
68
- : { height: hoverBridge } })), showBothHorizontalHoverBridges && (_jsx("div", { "aria-hidden": "true", className: "absolute bottom-0 right-0 top-0 translate-x-full bg-transparent", style: { width: hoverBridge } })), children] }), portalContainer);
78
+ : { height: hoverBridge } })), showBothHorizontalHoverBridges && (
79
+ // 横向弹窗可能动态从右改到左,这里左右都补桥,避免鼠标移过去时弹窗关闭。
80
+ _jsx("div", { "aria-hidden": "true", className: "absolute bottom-0 right-0 top-0 translate-x-full bg-transparent", style: { width: hoverBridge } })), children] }), portalContainer);
69
81
  }
@@ -6,6 +6,9 @@ export type SearchProps<T> = {
6
6
  defaultValue?: T;
7
7
  schemas: MOmit<FormFieldSchema<T>, 'rules' | 'required'>[];
8
8
  children?: ReactNode;
9
+ /**
10
+ * 获取表单实例的回调
11
+ */
9
12
  getForm?: (form: FormInstance<T>) => void;
10
13
  btnsClassName?: string;
11
14
  };
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  export type SelectOption = {
3
+ /** 展示内容;传 ReactNode 时建议同时提供 searchText 用于搜索 */
3
4
  label: React.ReactNode;
4
5
  value: string | number;
6
+ /** 搜索文本;优先级高于 label */
5
7
  searchText?: string;
6
8
  };
7
9
  export type SelectProps = {
@@ -12,8 +14,11 @@ export type SelectProps = {
12
14
  onChange?: (value: string | number | undefined) => void;
13
15
  className?: string;
14
16
  disabled?: boolean;
17
+ /** 开启清除 默认开启*/
15
18
  clearable?: boolean;
19
+ /** 是否支持搜索 默认开启 */
16
20
  searchable?: boolean;
21
+ /** 自定义弹出容器,传 parent 时挂载到当前 Select 的父元素,不传则默认挂载到 document.body */
17
22
  getPopupContainer?: 'parent' | (() => HTMLElement);
18
23
  };
19
24
  declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLDivElement>>;
@@ -34,6 +34,7 @@ const Select = forwardRef(({ options, value: controlledValue, defaultValue, plac
34
34
  container.style.position = previousPosition;
35
35
  };
36
36
  }, [isParentPopupContainer]);
37
+ // 合并 refs
37
38
  const setRefs = useCallback((node) => {
38
39
  selectRef.current = node;
39
40
  if (typeof ref === 'function') {
@@ -45,6 +46,7 @@ const Select = forwardRef(({ options, value: controlledValue, defaultValue, plac
45
46
  }, [ref]);
46
47
  const isControlled = controlledValue !== undefined;
47
48
  const value = isControlled ? controlledValue : internalValue;
49
+ // 获取当前选中的选项
48
50
  const selectedOption = options.find((opt) => opt.value === value);
49
51
  const displayText = selectedOption ? selectedOption.label : placeholder || '';
50
52
  const filteredOptions = useMemo(() => {
@@ -72,6 +74,7 @@ const Select = forwardRef(({ options, value: controlledValue, defaultValue, plac
72
74
  containerRef: isParentPopupContainer ? popupContainerRef : undefined,
73
75
  positionMode: isParentPopupContainer ? 'absolute' : 'fixed',
74
76
  });
77
+ // 展开时更新位置并获取焦点
75
78
  useEffect(() => {
76
79
  if (isOpen) {
77
80
  requestAnimationFrame(() => {
@@ -103,6 +106,7 @@ const Select = forwardRef(({ options, value: controlledValue, defaultValue, plac
103
106
  event.stopPropagation();
104
107
  if (disabled)
105
108
  return;
109
+ // 清除时 重置内外状态
106
110
  setInternalValue(undefined);
107
111
  onChange?.(undefined);
108
112
  }, [disabled, onChange]);
@@ -1,9 +1,24 @@
1
1
  import type { HTMLAttributes } from 'react';
2
2
  export type SkeletonMode = 'pageloading' | 'tableloading' | 'componentsloading';
3
3
  export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
4
+ /**
5
+ * 骨架屏模式
6
+ * @default 'componentsloading'
7
+ */
4
8
  mode?: SkeletonMode;
9
+ /**
10
+ * 行数(仅对 pageloading 和 tableloading 有效)
11
+ */
5
12
  rows?: number;
13
+ /**
14
+ * 是否显示标题骨架(仅对 pageloading 有效)
15
+ * @default true
16
+ */
6
17
  showTitle?: boolean;
18
+ /**
19
+ * 是否显示操作栏骨架(仅对 pageloading 有效)
20
+ * @default true
21
+ */
7
22
  showActions?: boolean;
8
23
  }
9
24
  export declare const Skeleton: {
@@ -7,7 +7,7 @@ const PageLoadingSkeleton = ({ rows = 5, showTitle = true, showActions = true })
7
7
  return (_jsxs("div", { className: "w-full space-y-6 p-6", children: [showTitle && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(SkeletonItem, { className: "h-8 w-48" }), showActions && (_jsxs("div", { className: "flex gap-3", children: [_jsx(SkeletonItem, { className: "h-10 w-24" }), _jsx(SkeletonItem, { className: "h-10 w-24" })] }))] })), _jsxs("div", { className: "flex gap-4", children: [_jsx(SkeletonItem, { className: "h-10 flex-1" }), _jsx(SkeletonItem, { className: "h-10 w-24" }), _jsx(SkeletonItem, { className: "h-10 w-24" })] }), _jsx("div", { className: "space-y-4", children: Array.from({ length: rows }).map((_, index) => (_jsxs("div", { className: "flex gap-4", children: [_jsx(SkeletonItem, { className: "h-20 flex-1" }), _jsx(SkeletonItem, { className: "h-20 w-32" }), _jsx(SkeletonItem, { className: "h-20 w-32" })] }, index))) }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(SkeletonItem, { className: "h-4 w-32" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(SkeletonItem, { className: "h-8 w-8" }), _jsx(SkeletonItem, { className: "h-8 w-8" }), _jsx(SkeletonItem, { className: "h-8 w-8" })] })] })] }));
8
8
  };
9
9
  const TableLoadingSkeleton = ({ rows = 5 }) => {
10
- const columnCount = 6;
10
+ const columnCount = 6; // 默认列数,可以根据需要调整
11
11
  return (_jsx("div", { className: "w-full", children: _jsx("div", { className: "bg-background rounded-[20px] overflow-hidden", children: _jsxs(Table, { containerClassName: "rounded-b-none", children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: Array.from({ length: columnCount }).map((_, index) => (_jsx(TableHead, { children: _jsx(SkeletonText, { width: index === 0 ? 120 : index === columnCount - 1 ? 80 : 150 }) }, index))) }) }), _jsx(TableBody, { children: Array.from({ length: rows }).map((_, rowIndex) => (_jsx(TableRow, { children: Array.from({ length: columnCount }).map((_, colIndex) => (_jsx(TableCell, { children: colIndex === columnCount - 1 ? (_jsxs("div", { className: "flex gap-2", children: [_jsx(SkeletonItem, { className: "h-8 w-16" }), _jsx(SkeletonItem, { className: "h-8 w-16" })] })) : (_jsx(SkeletonText, { width: colIndex === 0 ? 120 : colIndex === 1 ? '80%' : 150 })) }, colIndex))) }, rowIndex))) })] }) }) }));
12
12
  };
13
13
  const ComponentsLoadingSkeleton = ({ rows = 3 }) => {
@@ -5,6 +5,10 @@ import { type DropdownItem } from '../Dropdown';
5
5
  import type { FormFieldSchema } from '../Form';
6
6
  import type { SearchProps } from '../Search/Search';
7
7
  export type TableSearchSchema<T> = MOmit<FormFieldSchema<T>, 'rules' | 'required'> & {
8
+ /**
9
+ * - 字段是否常驻
10
+ * - 如果都没有则去前四个
11
+ */
8
12
  isPermanent?: boolean;
9
13
  };
10
14
  type TableSearchProps<T> = MOmit<SearchProps<T>, 'children' | 'schemas'> & {
@@ -12,18 +16,51 @@ type TableSearchProps<T> = MOmit<SearchProps<T>, 'children' | 'schemas'> & {
12
16
  onReset?: () => void;
13
17
  renderActionBtnArea?: () => ReactNode;
14
18
  schemas: TableSearchSchema<T>[];
19
+ /**
20
+ * 导出Excel按钮的下拉菜单项
21
+ */
15
22
  exportMenuItems?: DropdownItem[];
16
23
  btnConfig?: Partial<{
24
+ /**
25
+ * 导出Excel按钮的文案
26
+ */
17
27
  export?: ReactNode;
28
+ /**
29
+ * 收起按钮的文案
30
+ */
18
31
  collapse?: ReactNode;
32
+ /**
33
+ * 展开按钮的icon
34
+ */
19
35
  expandIcon?: ReactNode;
36
+ /**
37
+ * 展开按钮的文案
38
+ */
20
39
  expand?: ReactNode;
40
+ /**
41
+ * 重置按钮的文案
42
+ */
21
43
  reset?: ReactNode;
44
+ /**
45
+ * 查询按钮的文案
46
+ */
22
47
  search?: ReactNode;
23
48
  }>;
49
+ /**
50
+ * 备选边界
51
+ */
24
52
  expandBoundaries?: number;
53
+ /**
54
+ *
55
+ * @param exportType 导出类型
56
+ * @param searchData 当前查询条件
57
+ * @returns
58
+ */
25
59
  onExport?: (exportType: ExportType, searchData: T) => void;
26
60
  additionalButtons?: ReactNode;
61
+ /**
62
+ * 默认是否展开 @default true
63
+ */
27
64
  defaultExpand?: boolean;
28
65
  };
29
66
  declare const TableSearch: <T extends object>(props: TableSearchProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -91,6 +91,7 @@ const TableSearch = (props) => {
91
91
  }, [expandBoundaries, schemas]);
92
92
  const showExpandedBtn = schemas.length > expandBoundaries;
93
93
  let SSchemas;
94
+ // 有展开
94
95
  if (showExpandedBtn) {
95
96
  SSchemas = isExpanded
96
97
  ? schemas
@@ -113,6 +114,8 @@ const TableSearch = (props) => {
113
114
  }
114
115
  return (_jsx("div", { className: "tc-ui-table-search", children: _jsxs(Search, { getForm: (form) => {
115
116
  formRef.current = form;
116
- }, schemas: SSchemas, ...searchProps, children: [showExpandedBtn && (_jsx(Button, { variant: "default", size: "sm", onClick: handleExpand, rightIcon: _jsx("span", { className: cn('transition-transform duration-200', isExpanded && 'rotate-180'), children: btnConfig.expandIcon }), children: isExpanded ? btnConfig.collapseBtn : btnConfig.expandBtn }, "expandedBtn")), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSearch, children: btnConfig.searchBtn }), _jsx(Button, { variant: "default", size: "sm", onClick: handleReset, children: btnConfig.resetBtn }), renderActionBtnArea ? (renderActionBtnArea()) : (_jsx(Dropdown, { items: defaultExportItems, placement: "bottom-end", children: _jsx(Button, { variant: "default", size: "sm", rightIcon: _jsx(ChevronDown, { className: "h-4 w-4" }), children: btnConfig.exportBtn }) })), additionalButtons] }) }));
117
+ }, schemas: SSchemas, ...searchProps, children: [showExpandedBtn && (_jsx(Button, { variant: "default", size: "sm", onClick: handleExpand, rightIcon: _jsx("span", { className: cn('transition-transform duration-200', isExpanded && 'rotate-180'), children: btnConfig.expandIcon }), children: isExpanded ? btnConfig.collapseBtn : btnConfig.expandBtn }, "expandedBtn")), _jsx(Button, { variant: "primary", size: "sm", onClick: handleSearch, children: btnConfig.searchBtn }), _jsx(Button, { variant: "default", size: "sm", onClick: handleReset, children: btnConfig.resetBtn }), renderActionBtnArea ? (renderActionBtnArea()) : (
118
+ // 导出Excel按钮
119
+ _jsx(Dropdown, { items: defaultExportItems, placement: "bottom-end", children: _jsx(Button, { variant: "default", size: "sm", rightIcon: _jsx(ChevronDown, { className: "h-4 w-4" }), children: btnConfig.exportBtn }) })), additionalButtons] }) }));
117
120
  };
118
121
  export default TableSearch;
@@ -1,20 +1,66 @@
1
1
  import type { ChangeEvent, TextareaHTMLAttributes } from "react";
2
2
  export type TextareaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "onChange" | "defaultValue"> & {
3
3
  value?: string;
4
+ /**
5
+ *
6
+ * @param v textarea value
7
+ * @param e changeEvent
8
+ */
4
9
  onChange?: (v: string, e: ChangeEvent<HTMLTextAreaElement>) => void;
10
+ /**
11
+ * 只有初始化时会使用
12
+ * 如果会改变请使用value
13
+ */
5
14
  defaultValue?: string;
15
+ /**
16
+ * 最大字符数限制
17
+ * @default 500
18
+ */
6
19
  maxLength?: number;
20
+ /**
21
+ * 是否显示字符计数器
22
+ * @default true
23
+ */
7
24
  showCount?: boolean;
25
+ /**
26
+ * 占位符文本
27
+ */
8
28
  placeholder?: string;
29
+ /**
30
+ * 原生 textarea 节点的自定义 className
31
+ */
9
32
  textareaClassName?: string;
10
33
  };
11
34
  declare const Textarea: import("react").ForwardRefExoticComponent<Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "defaultValue" | "onChange"> & {
12
35
  value?: string;
36
+ /**
37
+ *
38
+ * @param v textarea value
39
+ * @param e changeEvent
40
+ */
13
41
  onChange?: (v: string, e: ChangeEvent<HTMLTextAreaElement>) => void;
42
+ /**
43
+ * 只有初始化时会使用
44
+ * 如果会改变请使用value
45
+ */
14
46
  defaultValue?: string;
47
+ /**
48
+ * 最大字符数限制
49
+ * @default 500
50
+ */
15
51
  maxLength?: number;
52
+ /**
53
+ * 是否显示字符计数器
54
+ * @default true
55
+ */
16
56
  showCount?: boolean;
57
+ /**
58
+ * 占位符文本
59
+ */
17
60
  placeholder?: string;
61
+ /**
62
+ * 原生 textarea 节点的自定义 className
63
+ */
18
64
  textareaClassName?: string;
19
65
  } & import("react").RefAttributes<HTMLTextAreaElement>>;
20
66
  export { Textarea };
@@ -10,6 +10,7 @@ const Textarea = forwardRef(({ className, textareaClassName, value: controlledVa
10
10
  });
11
11
  const handleChange = useCallback((e) => {
12
12
  const newValue = e.target.value;
13
+ // 如果设置了 maxLength,限制输入长度
13
14
  if (maxLength && newValue.length > maxLength) {
14
15
  return;
15
16
  }
@@ -1,8 +1,24 @@
1
1
  export interface TooltipProps {
2
+ /**
3
+ * 提示内容
4
+ */
2
5
  content: React.ReactNode;
6
+ /**
7
+ * 触发元素
8
+ */
3
9
  children: React.ReactElement;
10
+ /**
11
+ * 提示位置
12
+ * @default 'right'
13
+ */
4
14
  placement?: "top" | "bottom" | "left" | "right";
15
+ /**
16
+ * 是否显示
17
+ */
5
18
  visible?: boolean;
19
+ /**
20
+ * 类名
21
+ */
6
22
  className?: string;
7
23
  }
8
24
  export declare function Tooltip({ content, children, placement, visible, className, }: TooltipProps): import("react/jsx-runtime").JSX.Element;
@@ -26,12 +26,18 @@ export function Tooltip({ content, children, placement = "right", visible, class
26
26
  }
27
27
  }, [visible]);
28
28
  useEffect(() => {
29
+ // 显示时
29
30
  if (isVisible && triggerRef.current && tooltipRef.current) {
31
+ // 使用 requestAnimationFrame 确保 DOM 已更新
30
32
  requestAnimationFrame(() => {
31
33
  if (!triggerRef.current || !tooltipRef.current)
32
34
  return;
33
35
  const triggerRect = triggerRef.current.getBoundingClientRect();
34
36
  const tooltipRect = tooltipRef.current.getBoundingClientRect();
37
+ // const scrollY = window.scrollY;
38
+ // const scrollX = window.scrollX;
39
+ // const viewportWidth = window.innerWidth;
40
+ // const viewportHeight = window.innerHeight;
35
41
  const padding = 8;
36
42
  let top = 0;
37
43
  let left = 0;
@@ -51,6 +57,7 @@ export function Tooltip({ content, children, placement = "right", visible, class
51
57
  left = triggerRect.left - tooltipRect.width - padding;
52
58
  break;
53
59
  case "right":
60
+ // 直接使用元素所在元素
54
61
  top = triggerRect.top;
55
62
  left = triggerRect.right + padding;
56
63
  break;
@@ -58,6 +65,7 @@ export function Tooltip({ content, children, placement = "right", visible, class
58
65
  setPosition({ top, left });
59
66
  });
60
67
  }
68
+ // 隐藏时 重置定位定制 去除重影
61
69
  if (!isVisible) {
62
70
  const top = -100, left = -100;
63
71
  setPosition({ top, left });
@@ -5,14 +5,20 @@ export interface TreeNode {
5
5
  children?: TreeNode[];
6
6
  }
7
7
  export interface TreeSelectProps {
8
+ /** 树形数据 */
8
9
  data: TreeNode[];
10
+ /** 已选中的节点 ID 数组 */
9
11
  value?: (string | number)[];
12
+ /** 选中状态改变回调,返回选中的 keys 和半选的 keys */
10
13
  onChange?: (keys: {
11
14
  checked: (string | number)[];
12
15
  indeterminate: (string | number)[];
13
16
  }) => void;
17
+ /** 节点文本字段名 */
14
18
  labelField?: 'name' | 'desctext';
19
+ /** 是否可以选择 */
15
20
  checkable?: boolean;
21
+ /** 自定义类名 */
16
22
  className?: string;
17
23
  }
18
24
  export declare function TreeSelect({ data, value, onChange, labelField, checkable, className, }: TreeSelectProps): import("react/jsx-runtime").JSX.Element;
@@ -21,6 +21,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
21
21
  traverse(data, null);
22
22
  return { nodeMap: nodeM, parentMap: parentM };
23
23
  }, [data]);
24
+ // 获取节点的所有子节点 ID
24
25
  const getDescendantIds = useCallback((node) => {
25
26
  const ids = [];
26
27
  const traverse = (n) => {
@@ -54,6 +55,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
54
55
  };
55
56
  checkedSet.forEach((id) => traverseUp(id));
56
57
  }, [nodeMap, parentMap]);
58
+ // 切换展开/折叠
57
59
  const toggleExpand = useCallback((nodeId) => {
58
60
  setExpandedKeys((prev) => {
59
61
  const newSet = new Set(prev);
@@ -91,6 +93,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
91
93
  traverse(data);
92
94
  return indeterminateKeys;
93
95
  }, [data]);
96
+ // 切换选中状态
94
97
  const toggleCheck = useCallback((node) => {
95
98
  if (!checkable || !onChange)
96
99
  return;
@@ -109,6 +112,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
109
112
  const indeterminateKeys = getIndeterminateKeys(nextChecked);
110
113
  onChange({ checked: Array.from(nextChecked), indeterminate: indeterminateKeys });
111
114
  }, [value, onChange, checkable, getDescendantIds, updateParentSelection, getIndeterminateKeys]);
115
+ // 判断节点是否选中
112
116
  const isNodeChecked = useCallback((nodeId) => {
113
117
  return value.includes(nodeId);
114
118
  }, [value]);
@@ -127,6 +131,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
127
131
  const { total, checked } = getDescendantCount(node);
128
132
  return checked > 0 && checked < total;
129
133
  }, [value]);
134
+ // 渲染树节点
130
135
  function renderTreeNode(node, level = 0) {
131
136
  const hasChildren = node.children && node.children.length > 0;
132
137
  const isExpanded = expandedKeys.has(node.id);
@@ -147,6 +152,7 @@ export function TreeSelect({ data, value = [], onChange, labelField = 'name', ch
147
152
  }
148
153
  }, children: labelField === 'desctext' ? node.desctext || node.name : node.name })] }), hasChildren && isExpanded && _jsx("div", { children: node.children.map((child) => renderTreeNode(child, level + 1)) })] }, node.id));
149
154
  }
155
+ // 如果没有数据
150
156
  if (!data || data.length === 0) {
151
157
  return _jsx("div", { className: cn('tc-ui-tree-select p-8 text-center text-sm text-muted-foreground', className), children: "No data" });
152
158
  }
@@ -1,13 +1,40 @@
1
1
  import type { ChangeEvent, ReactNode, RefObject } from 'react';
2
2
  export interface FileUploadProps {
3
+ /**
4
+ * 文件 input 的 ref,用于父组件重置 value 等
5
+ */
3
6
  inputRef?: RefObject<HTMLInputElement | null>;
7
+ /**
8
+ * input 的 accept 属性,默认接收任意文件
9
+ */
4
10
  accept?: string;
11
+ /**
12
+ * 是否支持多选
13
+ */
5
14
  multiple?: boolean;
15
+ /**
16
+ * 选择文件时的回调
17
+ */
6
18
  onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
19
+ /**
20
+ * 选择文件按钮文案
21
+ */
7
22
  selectLabel: ReactNode;
23
+ /**
24
+ * label 的自定义 className
25
+ */
8
26
  labelClassName?: string;
27
+ /**
28
+ * 是否展示清空按钮
29
+ */
9
30
  showClear?: boolean;
31
+ /**
32
+ * 清空按钮点击回调
33
+ */
10
34
  onClear?: () => void;
35
+ /**
36
+ * 清空按钮文案
37
+ */
11
38
  clearLabel?: ReactNode;
12
39
  }
13
40
  export declare function FileUpload({ inputRef, accept, multiple, onChange, selectLabel, labelClassName, showClear, onClear, clearLabel, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,7 @@ import { useI18n } from '../../i18n';
3
3
  import { useBreadcrumb } from '../../hooks/useBreadcrumb';
4
4
  import { useLanguage } from '../../hooks/useLanguage';
5
5
  import { cn } from '../../lib/utils';
6
+ // import useUserStore from '@tc/ui-react/stores/user'
6
7
  import { ChevronRight, MoreHorizontal } from 'lucide-react';
7
8
  import { forwardRef } from 'react';
8
9
  import { useNavigate } from 'react-router-dom';
@@ -46,13 +47,18 @@ export function SimpleBreadcrumb({ className }) {
46
47
  const handleBreadcrumbClose = (id) => {
47
48
  const itemToRemove = items.find((item) => item.id === id);
48
49
  const isActive = itemToRemove?.active;
50
+ // 先计算剩余的面包屑项(在删除之前)
49
51
  const remainingItems = items.filter((item) => item.id !== id);
52
+ // 立即删除该项,确保状态及时更新
50
53
  removeItem(id);
54
+ // 如果删除的是当前激活的项,需要跳转
51
55
  if (isActive) {
52
56
  if (remainingItems.length > 0) {
57
+ // 有剩余项,跳转到最后一个
53
58
  const targetItem = remainingItems.at(-1);
54
59
  const targetPath = targetItem.path;
55
60
  if (targetPath) {
61
+ // 使用 setTimeout 确保删除操作先完成
56
62
  setTimeout(() => {
57
63
  navigate(targetPath);
58
64
  setActiveItem(targetItem.id);
@@ -60,7 +66,10 @@ export function SimpleBreadcrumb({ className }) {
60
66
  }
61
67
  }
62
68
  else {
69
+ // 获取首页路径(第一个权限页面)
63
70
  setTimeout(() => {
71
+ // todo
72
+ // navigate(useUserStore.getState().userPagePermissions[0]?.menuUrl || '/')
64
73
  }, 0);
65
74
  }
66
75
  }
@@ -38,6 +38,12 @@ export type UseDropdownPositioningResult = {
38
38
  ready: boolean;
39
39
  updatePosition: () => void;
40
40
  };
41
+ /**
42
+ * 计算弹窗该放在哪。
43
+ *
44
+ * right/left:跟触发元素顶部对齐,往右或往左弹。
45
+ * bottom/top:放在触发元素下方或上方,再按 start/end 对齐。
46
+ */
41
47
  export declare const getDefaultDropdownPosition: DropdownPositioningStrategy;
42
48
  export declare function useDropdownPositioning({ open, anchorRef, contentRef, placement, offset, matchAnchorWidth, strategy, estimateSize, watchDeps, containerRef, positionMode, }: UseDropdownPositioningOptions): UseDropdownPositioningResult;
43
49
  //# sourceMappingURL=useDropdownPositioning.d.ts.map