@oj-module/runtime 0.2.18 → 0.2.23

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.
@@ -9,6 +9,10 @@ interface AccessControlProps {
9
9
  * 权限验证组件
10
10
  *
11
11
  * @param AccessControlProps 权限验证组件的属性
12
+ * @param AccessControlProps.type 权限类型,默认为 code
13
+ * @param AccessControlProps.codes 权限值,可以是字符串或字符串数组
14
+ * @param AccessControlProps.children 有权限时渲染的子组件
15
+ * @param AccessControlProps.fallback 无权限时显示的内容
12
16
  * @returns 若子组件存在,并且传入的权限值有效,则返回子组件;否则返回 null
13
17
  */
14
18
  export declare function AccessControl({ type, codes, children, fallback }: AccessControlProps): ReactNode;
@@ -8,10 +8,10 @@ export interface FullscreenButtonProps extends Omit<ButtonProps, "target"> {
8
8
  /**
9
9
  * 全屏按钮组件
10
10
  *
11
- * @param target 全屏目标元素
12
- * @param fullscreenIcon 全屏时图标
13
- * @param fullscreenExitIcon 退出全屏时图标
14
- * @param restProps 其他属性
11
+ * @param target 组件属性(解构自 FullscreenButtonProps)
12
+ * @param target.target 全屏目标元素
13
+ * @param target.fullscreenIcon 全屏时图标
14
+ * @param target.fullscreenExitIcon 退出全屏时图标
15
15
  * @returns 返回全屏按钮组件
16
16
  */
17
17
  export declare const FullscreenButton: React.FC<FullscreenButtonProps>;
@@ -5,7 +5,7 @@ import type { VisibleDomRect } from "../../utils/dom/index";
5
5
  * @en Get the style of the layout content area
6
6
  */
7
7
  export declare function useLayoutContentStyle(): {
8
- contentElement: import("#node_modules/@types/react").RefObject<HTMLDivElement | null>;
8
+ contentElementRef: import("#node_modules/@types/react").RefObject<HTMLDivElement | null>;
9
9
  overlayStyle: CSSProperties;
10
10
  visibleDomRect: VisibleDomRect | null;
11
11
  };
@@ -17,6 +17,7 @@ export declare const exceptionUnknownComponentPath = "/exception/not-found-compo
17
17
  * 子路径部署(/oj/wiki/...)下直接与 loginPath 比较恒为 false,401 时会重复 goLogin。
18
18
  * React 组件内请用 `useLocation()`(react-router 已剥离 basename),无需本函数。
19
19
  *
20
+ * @param pathname 原始路径(含部署 base,如 `location.pathname`)
20
21
  * @param base 部署 base,恒以 `/` 结尾;缺省取 `resolveSiteBase()`(部署期注入优先,
21
22
  * 见 utils/site-base.ts —— runtime 产物里的 `import.meta.env.BASE_URL` 是构建期
22
23
  * 内联的 `/`,子路径部署下直接用它恒错)
package/dist/runtime.js CHANGED
@@ -204,6 +204,7 @@ var init_site_base = __esmMin((() => {
204
204
  * 子路径部署(/oj/wiki/...)下直接与 loginPath 比较恒为 false,401 时会重复 goLogin。
205
205
  * React 组件内请用 `useLocation()`(react-router 已剥离 basename),无需本函数。
206
206
  *
207
+ * @param pathname 原始路径(含部署 base,如 `location.pathname`)
207
208
  * @param base 部署 base,恒以 `/` 结尾;缺省取 `resolveSiteBase()`(部署期注入优先,
208
209
  * 见 utils/site-base.ts —— runtime 产物里的 `import.meta.env.BASE_URL` 是构建期
209
210
  * 内联的 `/`,子路径部署下直接用它恒错)
@@ -303,10 +304,10 @@ function getAppInfo() {
303
304
  return {
304
305
  "pkg": {
305
306
  "name": "@oj-module/runtime",
306
- "version": "0.2.18",
307
+ "version": "0.2.23",
307
308
  "license": "MIT"
308
309
  },
309
- "lastBuildTime": "2026-10-21 22:40:24"
310
+ "lastBuildTime": "2026-10-22 18:50:31"
310
311
  };
311
312
  }
312
313
  var init_get_app_info = __esmMin((() => {}));
@@ -429,7 +430,7 @@ function PageError({ error, resetErrorBoundary }) {
429
430
  };
430
431
  useEffect(() => {
431
432
  if (enableDynamicTitle) document.title = t("exception.pageErrorTitle");
432
- }, [enableDynamicTitle]);
433
+ }, [enableDynamicTitle, t]);
433
434
  return /* @__PURE__ */ jsx(Result, {
434
435
  status: "error",
435
436
  icon: /* @__PURE__ */ jsx("div", {
@@ -3495,7 +3496,7 @@ function useCurrentRoute() {
3495
3496
  const matches = useMatches();
3496
3497
  return useMemo(() => {
3497
3498
  return matches[matches.length - 1];
3498
- }, [matches, location]);
3499
+ }, [matches]);
3499
3500
  }
3500
3501
  var init_use_current_route = __esmMin((() => {}));
3501
3502
  //#endregion
@@ -3595,7 +3596,7 @@ function useCssVar(name, root = defaultRoot, options = {}) {
3595
3596
  }), [name, root]);
3596
3597
  useEffect(() => {
3597
3598
  if (options?.initialValue) controls.set(options.initialValue);
3598
- }, [options?.initialValue]);
3599
+ }, [options?.initialValue, controls]);
3599
3600
  useEffect(() => {
3600
3601
  return () => controls.remove();
3601
3602
  }, [controls]);
@@ -3656,7 +3657,7 @@ var init_dom = __esmMin((() => {}));
3656
3657
  * @en Get the style of the layout content area
3657
3658
  */
3658
3659
  function useLayoutContentStyle() {
3659
- const contentElement = useRef(null);
3660
+ const contentElementRef = useRef(null);
3660
3661
  const [visibleDomRect, setVisibleDomRect] = useState(null);
3661
3662
  const resizeObserverRef = useRef(null);
3662
3663
  const contentHeightControls = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT);
@@ -3673,7 +3674,7 @@ function useLayoutContentStyle() {
3673
3674
  };
3674
3675
  }, [visibleDomRect]);
3675
3676
  const debouncedCalcHeight = useDebounceFn((_entries) => {
3676
- const rect = getElementVisibleRect(contentElement.current);
3677
+ const rect = getElementVisibleRect(contentElementRef.current);
3677
3678
  setVisibleDomRect(rect);
3678
3679
  if (rect) {
3679
3680
  contentHeightControls.set(`${rect.height}px`);
@@ -3681,10 +3682,10 @@ function useLayoutContentStyle() {
3681
3682
  }
3682
3683
  }, { wait: 16 });
3683
3684
  useEffect(() => {
3684
- if (contentElement.current && !resizeObserverRef.current) {
3685
+ if (contentElementRef.current && !resizeObserverRef.current) {
3685
3686
  const resizeObserver = new ResizeObserver(debouncedCalcHeight.run);
3686
3687
  resizeObserverRef.current = resizeObserver;
3687
- resizeObserver.observe(contentElement.current);
3688
+ resizeObserver.observe(contentElementRef.current);
3688
3689
  }
3689
3690
  return () => {
3690
3691
  resizeObserverRef.current?.disconnect();
@@ -3692,7 +3693,7 @@ function useLayoutContentStyle() {
3692
3693
  };
3693
3694
  }, [debouncedCalcHeight]);
3694
3695
  return {
3695
- contentElement,
3696
+ contentElementRef,
3696
3697
  overlayStyle,
3697
3698
  visibleDomRect
3698
3699
  };
@@ -4173,7 +4174,7 @@ function KeepAliveLayer() {
4173
4174
  (aliveRef.current?.getCacheNodes?.())?.forEach((node) => {
4174
4175
  if (!openTabs.has(node.cacheKey)) aliveRef.current?.destroy(node.cacheKey);
4175
4176
  });
4176
- }, [openTabs]);
4177
+ }, [openTabs, aliveRef]);
4177
4178
  /**
4178
4179
  * 关闭多 tab 功能时,清空所有非当前页面缓存
4179
4180
  */
@@ -4181,10 +4182,18 @@ function KeepAliveLayer() {
4181
4182
  if (!tabbarEnable) (aliveRef.current?.getCacheNodes?.())?.forEach((node) => {
4182
4183
  if (node.cacheKey !== cacheKey) aliveRef.current?.destroy(node.cacheKey);
4183
4184
  });
4184
- }, [tabbarEnable]);
4185
+ }, [
4186
+ tabbarEnable,
4187
+ aliveRef,
4188
+ cacheKey
4189
+ ]);
4185
4190
  useEffect(() => {
4186
4191
  if (tabbarEnable && isRefresh) aliveRef.current?.refresh();
4187
- }, [isRefresh]);
4192
+ }, [
4193
+ isRefresh,
4194
+ aliveRef,
4195
+ tabbarEnable
4196
+ ]);
4188
4197
  const keepAliveExclude = useMemo(() => {
4189
4198
  /**
4190
4199
  * 不开启多 tab 时不需要 KeepAlive,把所有路由放进 exclude 仅保留切换动画
@@ -4212,7 +4221,7 @@ var init_keep_alive_layer = __esmMin((() => {
4212
4221
  //#region src/layout/layout-content/index.tsx
4213
4222
  function LayoutContent() {
4214
4223
  const { token: { colorBgLayout } } = theme.useToken();
4215
- const { contentElement } = useLayoutContentStyle();
4224
+ const { contentElementRef } = useLayoutContentStyle();
4216
4225
  const enableFooter = usePreferencesStore((state) => state.enableFooter);
4217
4226
  const fixedFooter = usePreferencesStore((state) => state.fixedFooter);
4218
4227
  /**
@@ -4221,7 +4230,7 @@ function LayoutContent() {
4221
4230
  */
4222
4231
  return /* @__PURE__ */ jsx("main", {
4223
4232
  id: ELEMENT_ID_MAIN_CONTENT,
4224
- ref: contentElement,
4233
+ ref: contentElementRef,
4225
4234
  className: "relative overflow-y-auto overflow-x-hidden grow",
4226
4235
  style: { backgroundColor: colorBgLayout },
4227
4236
  children: /* @__PURE__ */ jsx(Scrollbar, { children: /* @__PURE__ */ jsx(GlobalSpin, { children: /* @__PURE__ */ jsxs("div", {
@@ -4447,7 +4456,11 @@ function GlobalSearch() {
4447
4456
  useKeyPress("downarrow", handleDown);
4448
4457
  useEffect(() => {
4449
4458
  if (!keyword.length && Array.isArray(searchHistory)) setResultOptions(searchMenuList.filter((item) => searchHistory?.includes(item.key)));
4450
- }, [keyword, searchHistory]);
4459
+ }, [
4460
+ keyword,
4461
+ searchHistory,
4462
+ searchMenuList
4463
+ ]);
4451
4464
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
4452
4465
  onClick: () => setOpen((open) => !open),
4453
4466
  className: "group flex justify-center items-center gap-2 md:bg-colorBgLayout px-3 py-1.5 rounded-full cursor-pointer text-colorTextSecondary hover:text-colorText md:mr-2.5",
@@ -5113,18 +5126,18 @@ function Sidebar() {
5113
5126
  name: item.name,
5114
5127
  checked: item.value,
5115
5128
  onChange: handleChange,
5116
- children: item.label,
5117
- disabled: item.disabled
5129
+ disabled: item.disabled,
5130
+ children: item.label
5118
5131
  }, item.name);
5119
5132
  }),
5120
5133
  /* @__PURE__ */ jsx(SwitchItem, {
5121
5134
  name: "sidebarTheme",
5122
5135
  checked: sidebarTheme === "light",
5123
5136
  onChange: (name, value) => setPreferences(name, value ? "light" : "dark"),
5124
- children: t("preferences.sidebar.sidebarTheme"),
5125
5137
  disabled: !sidebarEnable,
5126
5138
  unCheckedChildren: /* @__PURE__ */ jsx(RiContrastFill, {}),
5127
- checkedChildren: /* @__PURE__ */ jsx(RiMoonIcon, {})
5139
+ checkedChildren: /* @__PURE__ */ jsx(RiMoonIcon, {}),
5140
+ children: t("preferences.sidebar.sidebarTheme")
5128
5141
  }),
5129
5142
  /* @__PURE__ */ jsx(NumberInputSpinner, {
5130
5143
  min: 40,
@@ -5702,7 +5715,7 @@ function UserMenu({ ...restProps }) {
5702
5715
  }
5703
5716
  if (key === "personal-center") navigate("/personal-center/my-profile");
5704
5717
  };
5705
- const altView = useMemo(() => isWindowsOs() ? "Alt" : "⌥", [isWindowsOs]);
5718
+ const altView = useMemo(() => isWindowsOs() ? "Alt" : "⌥", []);
5706
5719
  const items = [{
5707
5720
  label: t("personal-center:menu.personalCenter"),
5708
5721
  key: "personal-center",
@@ -5973,7 +5986,7 @@ function LayoutMenu({ mode = "inline", autoExpandCurrentMenu, handleMenuSelect,
5973
5986
  isMobile,
5974
5987
  sidebarCollapsed
5975
5988
  ]);
5976
- const handleOpenChange = (keys) => {
5989
+ const handleOpenChange = useCallback((keys) => {
5977
5990
  /**
5978
5991
  * 1. 手风琴模式,点击菜单项,自动关闭其他菜单
5979
5992
  * 2. 非手风琴模式且菜单是收起的,鼠标悬浮菜单自动关闭其他菜单
@@ -5992,7 +6005,12 @@ function LayoutMenu({ mode = "inline", autoExpandCurrentMenu, handleMenuSelect,
5992
6005
  if (currentCloseKey) setOpenKeys(menuParentKeys[currentCloseKey]);
5993
6006
  }
5994
6007
  } else setOpenKeys(keys);
5995
- };
6008
+ }, [
6009
+ accordion,
6010
+ sidebarCollapsed,
6011
+ openKeys,
6012
+ menuParentKeys
6013
+ ]);
5996
6014
  const menuOpenProps = useMemo(() => {
5997
6015
  if (autoExpandCurrentMenu) return {
5998
6016
  openKeys,
@@ -6019,7 +6037,8 @@ function LayoutMenu({ mode = "inline", autoExpandCurrentMenu, handleMenuSelect,
6019
6037
  }, [
6020
6038
  matches,
6021
6039
  sidebarCollapsed,
6022
- getSelectedKeys
6040
+ getSelectedKeys,
6041
+ accordion
6023
6042
  ]);
6024
6043
  return /* @__PURE__ */ jsx(Menu, {
6025
6044
  /**
@@ -6552,7 +6571,6 @@ function useDropdownMenu() {
6552
6571
  }, [
6553
6572
  t,
6554
6573
  activeKey,
6555
- homePath,
6556
6574
  openTabs
6557
6575
  ]);
6558
6576
  /**
@@ -6569,6 +6587,8 @@ function useDropdownMenu() {
6569
6587
  [TabActionKeys.CLOSE_OTHERS]: closeOtherTabs,
6570
6588
  [TabActionKeys.CLOSE_ALL]: closeAllTabs
6571
6589
  }), [
6590
+ refresh,
6591
+ setIsRefresh,
6572
6592
  removeTab,
6573
6593
  closeRightTabs,
6574
6594
  closeLeftTabs,
@@ -6814,7 +6834,7 @@ function LayoutTabbar() {
6814
6834
  hash: ""
6815
6835
  };
6816
6836
  navigate(key + historyState.search + historyState.hash);
6817
- }, [openTabs]);
6837
+ }, [openTabs, navigate]);
6818
6838
  /**
6819
6839
  * 处理标签页编辑(关闭)
6820
6840
  * @param {React.MouseEvent | React.KeyboardEvent | string} key - 被编辑的标签页的key
@@ -6941,7 +6961,8 @@ function LayoutTabbar() {
6941
6961
  location,
6942
6962
  currentRoute,
6943
6963
  setActiveKey,
6944
- addTab
6964
+ addTab,
6965
+ activeKey
6945
6966
  ]);
6946
6967
  return /* @__PURE__ */ jsx("div", {
6947
6968
  className: classes.tabsContainer,
@@ -7023,7 +7044,7 @@ function ContainerLayout() {
7023
7044
  if (screens.lg && !screens.xl) setPreferences("sidebarCollapsed", true);
7024
7045
  else if (screens.xl) setPreferences("sidebarCollapsed", false);
7025
7046
  else if (screens.xs || screens.sm && !screens.md) setPreferences("sidebarCollapsed", false);
7026
- }, [screens]);
7047
+ }, [screens, setPreferences]);
7027
7048
  const sidebarEnableState = useMemo(() => !isTopNav && sidebarEnable, [isTopNav, sidebarEnable]);
7028
7049
  const computedSidebarWidth = useMemo(() => {
7029
7050
  if (isMaximize || isMobile) return 0;
@@ -7049,7 +7070,7 @@ function ContainerLayout() {
7049
7070
  let height = 48;
7050
7071
  if (tabbarEnable) height += 35;
7051
7072
  return height;
7052
- }, [tabbarEnable, 35]);
7073
+ }, [tabbarEnable]);
7053
7074
  useEffect(() => {
7054
7075
  setLayoutHeaderHeight(isMaximize ? 35 : headerWrapperHeight);
7055
7076
  }, [headerWrapperHeight, isMaximize]);
@@ -7934,7 +7955,7 @@ async function generateRoutesFromBackend(backendRoutes) {
7934
7955
  /**
7935
7956
  * 转换路由配置
7936
7957
  * @param route 原始路由配置
7937
- * @param parentPath 父级路径(用于嵌套路由)
7958
+ * @param parentComponentPath 父级组件路径(用于嵌套路由)
7938
7959
  * @returns 转换后的路由配置
7939
7960
  */
7940
7961
  const transformRoute = async (route, parentComponentPath) => {
@@ -8918,8 +8939,6 @@ var init_remember_route = __esmMin((() => {
8918
8939
  * 必须带上部署 base(`resolveSiteBase()`):子路径部署下 `location.href = "/login"`
8919
8940
  * 会跳到宿主机根,而不是 `/oj/login`。不能用 `import.meta.env.BASE_URL`——预构建
8920
8941
  * 产物里它是构建期内联的 `/`。
8921
- *
8922
- * @returns 无返回值
8923
8942
  */
8924
8943
  function goLogin() {
8925
8944
  useAuthStore.getState().reset();
@@ -9022,6 +9041,8 @@ function onRefreshFailed(error) {
9022
9041
  * 订阅者对象应包含 resolve 和 reject 方法。
9023
9042
  *
9024
9043
  * @param subscriber 订阅者对象,包含 resolve 和 reject 方法
9044
+ * @param subscriber.resolve 当 token 刷新成功时调用的函数,传入新的 token
9045
+ * @param subscriber.reject 当 token 刷新失败时调用的函数,传入错误信息
9025
9046
  */
9026
9047
  function addRefreshSubscriber(subscriber) {
9027
9048
  refreshSubscribers.push(subscriber);
@@ -9447,6 +9468,10 @@ function useAccess() {
9447
9468
  * 权限验证组件
9448
9469
  *
9449
9470
  * @param AccessControlProps 权限验证组件的属性
9471
+ * @param AccessControlProps.type 权限类型,默认为 code
9472
+ * @param AccessControlProps.codes 权限值,可以是字符串或字符串数组
9473
+ * @param AccessControlProps.children 有权限时渲染的子组件
9474
+ * @param AccessControlProps.fallback 无权限时显示的内容
9450
9475
  * @returns 若子组件存在,并且传入的权限值有效,则返回子组件;否则返回 null
9451
9476
  */
9452
9477
  function AccessControl({ type = "code", codes, children, fallback }) {
@@ -9800,7 +9825,7 @@ function BasicTable(props) {
9800
9825
  if (enableFooter && fixedFooter) return 40;
9801
9826
  return 0;
9802
9827
  }, [enableFooter, fixedFooter]);
9803
- const getPaginationProps = () => {
9828
+ const getPaginationProps = useCallback(() => {
9804
9829
  if (props.pagination === false) return false;
9805
9830
  return {
9806
9831
  placement: ["bottomStart"],
@@ -9810,7 +9835,7 @@ function BasicTable(props) {
9810
9835
  showTotal: (total) => t("common.pagination", { total }),
9811
9836
  ...props.pagination
9812
9837
  };
9813
- };
9838
+ }, [props.pagination, t]);
9814
9839
  /**
9815
9840
  * @description 计算分页器的高度
9816
9841
  * 如果分页器被禁用,则返回 0,否则根据分页器的大小返回相应的高度
@@ -4,7 +4,5 @@
4
4
  * 必须带上部署 base(`resolveSiteBase()`):子路径部署下 `location.href = "/login"`
5
5
  * 会跳到宿主机根,而不是 `/oj/login`。不能用 `import.meta.env.BASE_URL`——预构建
6
6
  * 产物里它是构建期内联的 `/`。
7
- *
8
- * @returns 无返回值
9
7
  */
10
8
  export declare function goLogin(): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/runtime",
3
3
  "type": "module",
4
- "version": "0.2.18",
4
+ "version": "0.2.23",
5
5
  "description": "React Antd Admin 框架运行时,供模块工程消费",
6
6
  "license": "MIT",
7
7
  "imports": {