@_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.
- package/cjs/bundler/utils.js +13 -5
- package/cjs/packages/core/index.js +1 -1
- package/cjs/packages/core/loader/config.js +4 -2
- package/cjs/packages/core/loader/controller.js +2 -3
- package/cjs/packages/core/loader/extend.js +1 -2
- package/cjs/packages/core/loader/middleware.js +2 -3
- package/cjs/packages/core/loader/model.js +7 -3
- package/cjs/packages/core/loader/router-schema.js +4 -2
- package/cjs/packages/core/loader/router.js +10 -6
- package/cjs/packages/core/loader/service.js +2 -2
- package/cjs/packages/utils/runFileFn.js +46 -1
- package/esm/bundler/utils.js +13 -5
- package/esm/packages/core/index.js +1 -2
- package/esm/packages/core/loader/config.js +4 -3
- package/esm/packages/core/loader/controller.js +2 -4
- package/esm/packages/core/loader/extend.js +1 -3
- package/esm/packages/core/loader/middleware.js +2 -4
- package/esm/packages/core/loader/model.js +7 -4
- package/esm/packages/core/loader/router-schema.js +4 -3
- package/esm/packages/core/loader/router.js +10 -7
- package/esm/packages/core/loader/service.js +2 -3
- package/esm/packages/utils/runFileFn.js +47 -1
- package/fe/frontend/dash/Dashboard.js +11 -1
- package/fe/frontend/dash/dash.entry.js +19 -1
- package/fe/frontend/main.js +2 -0
- package/fe/frontend/widgets/common/CRUD/CRUD.js +1 -0
- package/fe/frontend/widgets/common/importComponent.js +1 -0
- package/fe/frontend/widgets/common/language.js +1 -0
- package/fe/frontend/widgets/common/menu.d.ts +12 -0
- package/fe/frontend/widgets/common/menu.js +21 -0
- package/fe/frontend/widgets/common/request.d.ts +3 -0
- package/fe/frontend/widgets/common/request.js +6 -0
- package/fe/frontend/widgets/components/BasePage/HeaderView.js +3 -1
- package/fe/frontend/widgets/defaultPages/Schema/components/CallCom/PopFrom.js +15 -0
- package/fe/frontend/widgets/defaultPages/Schema/components/SchemaSearch/index.js +5 -0
- package/fe/frontend/widgets/defaultPages/Schema/components/SchemaTable/index.js +13 -1
- package/fe/frontend/widgets/defaultPages/Schema/data/eventInfo.js +4 -4
- package/fe/frontend/widgets/defaultPages/Schema/index.js +7 -0
- package/fe/frontend/widgets/defaultPages/Schema/schemaType.d.ts +9 -0
- package/fe/frontend/widgets/defaultPages/Schema/stores/schemaEventBus.d.ts +19 -0
- package/fe/frontend/widgets/defaultPages/Schema/stores/schemaEventBus.js +4 -0
- package/fe/frontend/widgets/defaultPages/Schema/stores/schemaStore.d.ts +14 -0
- package/fe/frontend/widgets/defaultPages/Schema/stores/schemaStore.js +2 -0
- package/fe/frontend/widgets/defaultPages/Schema/utils/schemaConversion.js +43 -0
- package/fe/frontend/widgets/defaultPages/Schema/utils/validator.d.ts +3 -0
- package/fe/frontend/widgets/defaultPages/Schema/utils/validator.js +5 -0
- package/fe/frontend/widgets/defaultPages/SidebarSlotPage/SidebarSlotContainer.js +7 -0
- package/fe/frontend/widgets/defaultPages/SidebarSlotPage/index.js +1 -0
- package/fe/frontend/widgets/defaultPages/SlotPage/index.js +2 -0
- package/fe/frontend/widgets/hooks/useCurrentMenuData.d.ts +8 -0
- package/fe/frontend/widgets/hooks/useCurrentMenuData.js +8 -0
- package/fe/frontend/widgets/store/mode.d.ts +3 -0
- package/fe/frontend/widgets/store/mode.js +1 -0
- package/fe/model/types/data/button.d.ts +9 -0
- package/fe/model/types/data/button.js +15 -0
- package/fe/model/types/data/component.d.ts +24 -0
- package/fe/model/types/data/component.js +10 -0
- package/fe/model/types/data/fetchInfo.d.ts +12 -0
- package/fe/model/types/data/schema.d.ts +51 -0
- package/fe/model/types/menuType.d.ts +29 -0
- package/fe/model/types/model.d.ts +13 -0
- package/fe/packages/common/i18n/default.d.ts +5 -0
- package/fe/packages/common/i18n/default.js +5 -0
- package/fe/packages/common/i18n/en-US.d.ts +5 -0
- package/fe/packages/common/i18n/en-US.js +5 -0
- package/fe/packages/common/i18n/index.d.ts +24 -0
- package/fe/packages/common/i18n/index.js +31 -0
- package/fe/packages/common/i18n/types.d.ts +24 -0
- package/fe/packages/ui/react/components/Button/Button.d.ts +33 -0
- package/fe/packages/ui/react/components/Button/Button.js +3 -0
- package/fe/packages/ui/react/components/Button/SumbitButton.d.ts +4 -0
- package/fe/packages/ui/react/components/Button/SumbitButton.js +4 -0
- package/fe/packages/ui/react/components/ConfirmDialog/ConfirmDialog.d.ts +8 -0
- package/fe/packages/ui/react/components/DataTable/index.d.ts +19 -0
- package/fe/packages/ui/react/components/DataTable/index.js +4 -0
- package/fe/packages/ui/react/components/Date/Calendar.d.ts +13 -0
- package/fe/packages/ui/react/components/Date/Calendar.js +10 -1
- package/fe/packages/ui/react/components/Date/Date.d.ts +11 -0
- package/fe/packages/ui/react/components/Date/Date.js +19 -0
- package/fe/packages/ui/react/components/Date/LocaleContext.d.ts +4 -0
- package/fe/packages/ui/react/components/Date/LocaleContext.js +4 -0
- package/fe/packages/ui/react/components/Date/LocaleProvider.d.ts +11 -0
- package/fe/packages/ui/react/components/Date/LocaleProvider.js +11 -0
- package/fe/packages/ui/react/components/Date/TimePicker.js +1 -0
- package/fe/packages/ui/react/components/Date/dateLocaleStore.d.ts +6 -0
- package/fe/packages/ui/react/components/Date/locales.d.ts +19 -0
- package/fe/packages/ui/react/components/Date/locales.js +9 -0
- package/fe/packages/ui/react/components/Drawer/Drawer.d.ts +6 -0
- package/fe/packages/ui/react/components/Dropdown/Dropdown.d.ts +1 -0
- package/fe/packages/ui/react/components/Form/Form.d.ts +6 -0
- package/fe/packages/ui/react/components/Form/FormItem.d.ts +21 -0
- package/fe/packages/ui/react/components/Form/FormItem.js +8 -1
- package/fe/packages/ui/react/components/Form/SchemaForm/data.js +1 -0
- package/fe/packages/ui/react/components/Form/SchemaForm/index.d.ts +93 -0
- package/fe/packages/ui/react/components/Form/SchemaForm/index.js +5 -1
- package/fe/packages/ui/react/components/ImagePreview/ImagePreview.js +8 -0
- package/fe/packages/ui/react/components/ImagePreview/PreviewImage.d.ts +3 -0
- package/fe/packages/ui/react/components/Input/Input.d.ts +22 -0
- package/fe/packages/ui/react/components/Input/Input.js +3 -0
- package/fe/packages/ui/react/components/InputNumber/InputNumber.d.ts +2 -0
- package/fe/packages/ui/react/components/Label/Label.d.ts +29 -0
- package/fe/packages/ui/react/components/Label/Label.js +2 -0
- package/fe/packages/ui/react/components/Menu/Menu.js +4 -0
- package/fe/packages/ui/react/components/Menu/SubMenu.d.ts +7 -0
- package/fe/packages/ui/react/components/Menu/SubMenu.js +46 -1
- package/fe/packages/ui/react/components/Menu/menuTypes.d.ts +1 -0
- package/fe/packages/ui/react/components/Message/Message.d.ts +7 -0
- package/fe/packages/ui/react/components/Message/Message.js +3 -0
- package/fe/packages/ui/react/components/Message/MessageManager.js +8 -0
- package/fe/packages/ui/react/components/Modal/Modal.d.ts +6 -0
- package/fe/packages/ui/react/components/Modal/Modal.js +1 -0
- package/fe/packages/ui/react/components/Modal/ModalManager.d.ts +12 -0
- package/fe/packages/ui/react/components/Modal/ModalManager.js +4 -1
- package/fe/packages/ui/react/components/Overlay/Overlay.d.ts +3 -0
- package/fe/packages/ui/react/components/Pagination/Pagination.d.ts +7 -0
- package/fe/packages/ui/react/components/Pagination/Pagination.js +8 -1
- package/fe/packages/ui/react/components/Popup/Popup.js +14 -2
- package/fe/packages/ui/react/components/Search/Search.d.ts +3 -0
- package/fe/packages/ui/react/components/Select/Select.d.ts +5 -0
- package/fe/packages/ui/react/components/Select/Select.js +4 -0
- package/fe/packages/ui/react/components/Skeleton/Skeleton.d.ts +15 -0
- package/fe/packages/ui/react/components/Skeleton/Skeleton.js +1 -1
- package/fe/packages/ui/react/components/TableSearch/TableSearch.d.ts +37 -0
- package/fe/packages/ui/react/components/TableSearch/TableSearch.js +4 -1
- package/fe/packages/ui/react/components/Textarea/Textarea.d.ts +46 -0
- package/fe/packages/ui/react/components/Textarea/Textarea.js +1 -0
- package/fe/packages/ui/react/components/Tooltip/Tooltip.d.ts +16 -0
- package/fe/packages/ui/react/components/Tooltip/Tooltip.js +8 -0
- package/fe/packages/ui/react/components/TreeSelect/TreeSelect.d.ts +6 -0
- package/fe/packages/ui/react/components/TreeSelect/TreeSelect.js +6 -0
- package/fe/packages/ui/react/components/Upload/Upload.d.ts +27 -0
- package/fe/packages/ui/react/components/breadcrumb/breadcrumb.js +9 -0
- package/fe/packages/ui/react/components/hooks/useDropdownPositioning.d.ts +6 -0
- package/fe/packages/ui/react/components/hooks/useDropdownPositioning.js +14 -0
- package/fe/packages/ui/react/components/hooks/useInputController.d.ts +3 -0
- package/fe/packages/ui/react/components/hooks/useInputController.js +7 -0
- package/fe/packages/ui/react/components/testPage/MenuTestPage.js +3 -0
- package/fe/packages/ui/react/components/testPage/index.js +26 -0
- package/fe/packages/ui/react/hooks/useExecuteOnce.d.ts +19 -1
- package/fe/packages/ui/react/hooks/useExecuteOnce.js +22 -1
- package/fe/packages/ui/react/hooks/useRefState.d.ts +12 -0
- package/fe/packages/ui/react/hooks/useRefState.js +1 -0
- package/fe/packages/ui/react/hooks/useWatch.d.ts +8 -0
- package/fe/packages/ui/react/i18n/I18nProvider.d.ts +18 -0
- package/fe/packages/ui/react/i18n/useI18n.d.ts +4 -0
- package/fe/packages/ui/react/i18n/useI18n.js +4 -0
- package/fe/packages/ui/react/index.js +2 -0
- package/fe/packages/ui/react/lib/export.d.ts +44 -0
- package/fe/packages/ui/react/lib/export.js +40 -0
- package/fe/packages/ui/react/lib/utils.d.ts +24 -0
- package/fe/packages/ui/react/lib/utils.js +25 -0
- package/fe/packages/ui/react/stores/breadcrumb.js +2 -0
- package/model/index.d.ts +2 -0
- package/model/types/data/button.d.ts +32 -0
- package/model/types/data/component.d.ts +61 -0
- package/model/types/data/fetchInfo.d.ts +20 -0
- package/model/types/data/schema.d.ts +98 -0
- package/model/types/data/search.d.ts +7 -0
- package/model/types/index.d.ts +2 -0
- package/model/types/menuType.d.ts +73 -0
- package/model/types/model.d.ts +33 -0
- package/model/types/test.d.ts +2 -0
- package/package.json +10 -7
- 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
|
-
|
|
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 && (
|
|
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
|
}
|
|
@@ -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()) : (
|
|
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
|