@oj-module/cli 0.2.63 → 0.2.64

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/cli",
3
3
  "type": "module",
4
- "version": "0.2.63",
4
+ "version": "0.2.64",
5
5
  "description": "React Antd 模块工程命令行工具(含预构建宿主 shell-dist)",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@faker-js/faker": "^10.4.0",
52
- "@oj-module/runtime": "0.2.63",
52
+ "@oj-module/runtime": "0.2.64",
53
53
  "@redocly/cli": "^2.51.0",
54
54
  "@tailwindcss/vite": "^4.3.0",
55
55
  "cac": "^6.7.14",
@@ -1,5 +1,6 @@
1
1
  import { StyleProvider } from "@ant-design/cssinjs";
2
- import { AntdApp, ErrorBoundary, LayoutEffects, PageError, getHostRoutes, getRoutes, loadAll, resolveApiBase, setupI18n, useAccessStore, useAuthStore, usePreferences, useUserStore } from "@oj-module/runtime";
2
+ import { ProConfigProvider, enUSIntl, zhCNIntl } from "@ant-design/pro-components";
3
+ import { ANT_DESIGN_LOCALE, AntdApp, ErrorBoundary, LayoutEffects, PageError, getHostRoutes, getRoutes, loadAll, resolveApiBase, setupI18n, useAccessStore, useAuthStore, usePreferences, useUserStore } from "@oj-module/runtime";
3
4
  import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
5
  import { ConfigProvider, theme } from "antd";
5
6
  import { useEffect, useState } from "react";
@@ -443,11 +444,12 @@ basename: base });
443
444
  * window.$message/$modal/$notification。
444
445
  */
445
446
  function HostProviders() {
446
- const { isDark, themeColorPrimary, themeRadius, sideCollapsedWidth } = usePreferences();
447
+ const { language, isDark, themeColorPrimary, themeRadius, sideCollapsedWidth } = usePreferences();
447
448
  return /* @__PURE__ */ jsx(StyleProvider, {
448
449
  layer: true,
449
450
  hashPriority: "high",
450
451
  children: /* @__PURE__ */ jsx(ConfigProvider, {
452
+ locale: ANT_DESIGN_LOCALE[language],
451
453
  theme: {
452
454
  cssVar: {},
453
455
  hashed: false,
@@ -462,10 +464,13 @@ function HostProviders() {
462
464
  collapsedWidth: sideCollapsedWidth
463
465
  } }
464
466
  },
465
- children: /* @__PURE__ */ jsx(AntdApp, { children: /* @__PURE__ */ jsx(QueryClientProvider, {
466
- client: queryClient,
467
- children: /* @__PURE__ */ jsx(Boot, {})
468
- }) })
467
+ children: /* @__PURE__ */ jsx(ProConfigProvider, {
468
+ intl: language === "en-US" ? enUSIntl : zhCNIntl,
469
+ children: /* @__PURE__ */ jsx(AntdApp, { children: /* @__PURE__ */ jsx(QueryClientProvider, {
470
+ client: queryClient,
471
+ children: /* @__PURE__ */ jsx(Boot, {})
472
+ }) })
473
+ })
469
474
  })
470
475
  });
471
476
  }
@@ -15,8 +15,8 @@ import { App, Avatar, Badge, Breadcrumb, Button, Checkbox, Col, ColorPicker, Con
15
15
  import { Children, Suspense, cloneElement, createElement, forwardRef, isValidElement, lazy, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
16
16
  import { initReactI18next, useTranslation } from "react-i18next";
17
17
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
- import "antd/locale/en_US";
19
- import "antd/locale/zh_CN";
18
+ import enUS from "antd/locale/en_US";
19
+ import zhCN from "antd/locale/zh_CN";
20
20
  import i18next from "i18next";
21
21
  import { clsx } from "clsx";
22
22
  import { twMerge } from "tailwind-merge";
@@ -304,10 +304,10 @@ function getAppInfo() {
304
304
  return {
305
305
  "pkg": {
306
306
  "name": "@oj-module/runtime",
307
- "version": "0.2.63",
307
+ "version": "0.2.64",
308
308
  "license": "MIT"
309
309
  },
310
- "lastBuildTime": "2026-11-04 10:42:39"
310
+ "lastBuildTime": "2026-11-04 13:12:37"
311
311
  };
312
312
  }
313
313
  var init_get_app_info = __esmMin((() => {}));
@@ -597,7 +597,7 @@ var init_authority$1 = __esmMin((() => {
597
597
  privacyPolicy: privacyPolicy$1,
598
598
  termsOfService: termsOfService$1
599
599
  };
600
- })), appLoading$1, back$1, backHome$1, warning$1, confirm$1, fail$1, success$2, deleteSuccess$1, confirmDelete$1, view$1, edit$1, error$61, index$1, upload$1, noData$1, action$1, add$1, addSuccess$1, backToHome$1, batchDelete$1, cancel$1, close$1, check$2, expandColumn$1, columnSetting$1, config$2, keywordSearch$1, modify$1, modifySuccess$1, pleaseCheckValue$1, refresh$1, reset$1, search$3, tip$1, tipMessage$1, trigger$1, update$1, updateSuccess$1, userCenter$1, name$1, id$1, status$1, enabled$1, deactivated$1, activated$1, disabled$1, createTime$1, updateTime$1, remark$1, demoOnly$1, pagination$1, expandAll$1, collapseAll$1, checkAll$1, cancelAll$1, total$1, save$1, deleteFailed$1, common_default$1;
600
+ })), appLoading$1, back$1, backHome$1, warning$1, confirm$1, fail$1, success$2, deleteSuccess$1, confirmDelete$1, view$1, edit$1, error$61, index$1, upload$1, avatarChange$1, uploadSuccess$1, uploadFailed$1, noData$1, action$1, add$1, addSuccess$1, backToHome$1, batchDelete$1, cancel$1, close$1, check$2, expandColumn$1, columnSetting$1, config$2, keywordSearch$1, modify$1, modifySuccess$1, pleaseCheckValue$1, refresh$1, reset$1, search$3, tip$1, tipMessage$1, trigger$1, update$1, updateSuccess$1, userCenter$1, name$1, id$1, status$1, enabled$1, deactivated$1, activated$1, disabled$1, createTime$1, updateTime$1, remark$1, demoOnly$1, pagination$1, expandAll$1, collapseAll$1, checkAll$1, cancelAll$1, total$1, save$1, deleteFailed$1, common_default$1;
601
601
  var init_common$1 = __esmMin((() => {
602
602
  appLoading$1 = "资源正在加载中...";
603
603
  back$1 = "返回";
@@ -613,6 +613,9 @@ var init_common$1 = __esmMin((() => {
613
613
  error$61 = "错误";
614
614
  index$1 = "序号";
615
615
  upload$1 = "上传";
616
+ avatarChange$1 = "更换头像";
617
+ uploadSuccess$1 = "{{name}} 上传成功";
618
+ uploadFailed$1 = "{{name}} 上传失败";
616
619
  noData$1 = "暂无数据";
617
620
  action$1 = "操作";
618
621
  add$1 = "新增";
@@ -673,6 +676,9 @@ var init_common$1 = __esmMin((() => {
673
676
  error: error$61,
674
677
  index: index$1,
675
678
  upload: upload$1,
679
+ avatarChange: avatarChange$1,
680
+ uploadSuccess: uploadSuccess$1,
681
+ uploadFailed: uploadFailed$1,
676
682
  noData: noData$1,
677
683
  action: action$1,
678
684
  add: add$1,
@@ -1043,7 +1049,7 @@ var init_authority = __esmMin((() => {
1043
1049
  privacyPolicy,
1044
1050
  termsOfService
1045
1051
  };
1046
- })), appLoading, back, backHome, warning, confirm, fail, success$1, deleteSuccess, confirmDelete, view, edit, error$60, index, upload, noData, action, addSuccess, backToHome, batchDelete, cancel, close, check$1, expandColumn, columnSetting, config$1, keywordSearch, modify, modifySuccess, pleaseCheckValue, refresh, reset, search$1, tipMessage, trigger, update, updateSuccess, userCenter, name, status, enabled, deactivated, activated, disabled, createTime, updateTime, remark, demoOnly, pagination, expandAll, collapseAll, checkAll, cancelAll, total, save, deleteFailed, common_default;
1052
+ })), appLoading, back, backHome, warning, confirm, fail, success$1, deleteSuccess, confirmDelete, view, edit, error$60, index, upload, avatarChange, uploadSuccess, uploadFailed, noData, action, addSuccess, backToHome, batchDelete, cancel, close, check$1, expandColumn, columnSetting, config$1, keywordSearch, modify, modifySuccess, pleaseCheckValue, refresh, reset, search$1, tipMessage, trigger, update, updateSuccess, userCenter, name, status, enabled, deactivated, activated, disabled, createTime, updateTime, remark, demoOnly, pagination, expandAll, collapseAll, checkAll, cancelAll, total, save, deleteFailed, common_default;
1047
1053
  var init_common = __esmMin((() => {
1048
1054
  appLoading = "Loading Resources...";
1049
1055
  back = "Back";
@@ -1059,6 +1065,9 @@ var init_common = __esmMin((() => {
1059
1065
  error$60 = "Error";
1060
1066
  index = "Index";
1061
1067
  upload = "Upload";
1068
+ avatarChange = "Change Avatar";
1069
+ uploadSuccess = "{{name}} uploaded successfully";
1070
+ uploadFailed = "{{name}} upload failed";
1062
1071
  noData = "No data";
1063
1072
  action = "Action";
1064
1073
  addSuccess = "Added successfully";
@@ -1116,6 +1125,9 @@ var init_common = __esmMin((() => {
1116
1125
  error: error$60,
1117
1126
  index,
1118
1127
  upload,
1128
+ avatarChange,
1129
+ uploadSuccess,
1130
+ uploadFailed,
1119
1131
  noData,
1120
1132
  action,
1121
1133
  add: "Add",
@@ -1483,10 +1495,14 @@ function setupI18n() {
1483
1495
  document.documentElement.lang = lng;
1484
1496
  });
1485
1497
  }
1486
- var i18nResources, i18nInitOptions, i18n;
1498
+ var ANT_DESIGN_LOCALE, i18nResources, i18nInitOptions, i18n;
1487
1499
  var init_locales = __esmMin((() => {
1488
1500
  init_helper();
1489
1501
  init_t();
1502
+ ANT_DESIGN_LOCALE = {
1503
+ "zh-CN": zhCN,
1504
+ "en-US": enUS
1505
+ };
1490
1506
  i18nResources = {
1491
1507
  "zh-CN": { translation: getZhCnLang() },
1492
1508
  "en-US": { translation: getEnUsLang() }
@@ -9865,6 +9881,7 @@ function BasicContent(props) {
9865
9881
  init_api_provider();
9866
9882
  init_api_base();
9867
9883
  function FormAvatarItem({ value, onChange }) {
9884
+ const { t } = useTranslation();
9868
9885
  const uploadProvider = getUploadApiProvider();
9869
9886
  const uploadAction = uploadProvider?.action ?? `${resolveApiBase()}/upload`;
9870
9887
  const uploadHeaders = uploadProvider?.headers() ?? { authorization: "authorization-text" };
@@ -9887,13 +9904,13 @@ function FormAvatarItem({ value, onChange }) {
9887
9904
  headers: uploadHeaders,
9888
9905
  onChange: (info) => {
9889
9906
  if (info.file.status === "done") {
9890
- window.$message?.success(`${info.file.name} file uploaded successfully`);
9907
+ window.$message?.success(t("common.uploadSuccess", { name: info.file.name }));
9891
9908
  onChange?.(info.file.response?.data);
9892
- } else if (info.file.status === "error") window.$message?.error(`${info.file.name} file upload failed.`);
9909
+ } else if (info.file.status === "error") window.$message?.error(t("common.uploadFailed", { name: info.file.name }));
9893
9910
  },
9894
9911
  children: /* @__PURE__ */ jsx(Button, {
9895
9912
  icon: /* @__PURE__ */ jsx(UploadOutlined, {}),
9896
- children: "更换头像"
9913
+ children: t("common.avatarChange")
9897
9914
  })
9898
9915
  })
9899
9916
  })]
@@ -28291,4 +28308,4 @@ init_ri();
28291
28308
  init_menu_icons();
28292
28309
  init_tree();
28293
28310
  //#endregion
28294
- export { AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, DEFAULT_TENANT, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TENANT_HEADER, TENANT_QUERY_PARAM, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, bindingDiff, bootstrapTenant, canAssignRole, canDeleteRole, canEditRoleBindings, currentTenant, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchChangePassword, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLdapLogin, fetchLogin, fetchLoginMethods, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRbacAdminResetPassword, fetchRbacContactDetail, fetchRbacContacts, fetchRbacCreateRole, fetchRbacCreateUser, fetchRbacDeleteRole, fetchRbacOplog, fetchRbacOplogPost, fetchRbacPermissionList, fetchRbacRoleList, fetchRbacSetForbidden, fetchRbacSettings, fetchRbacUpdateBindings, fetchRbacUpdateSetting, fetchRbacUpdateUserRoles, fetchRbacUserList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchSolutionRoutes, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getHostRoutes, getModule, getModuleRoutePrefix, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, groupByModule, groupSettingsByModule, handleTree, loadAll, mapTree, menuIcons, notify, permissionPrefix, request, resolveApiBase, resolveSiteBase, roleLabel, setupI18n, traverseTreeValues, unloadModule, useAccess, useAccessStore, useAuthStore, usePreferences, useSlotNodes, useSolutionRoutes, useTabKey, useTabsStore, useTenantStore, useUserStore, withSiteBase, external_exports as z };
28311
+ export { ANT_DESIGN_LOCALE, AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, DEFAULT_TENANT, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TENANT_HEADER, TENANT_QUERY_PARAM, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, bindingDiff, bootstrapTenant, canAssignRole, canDeleteRole, canEditRoleBindings, currentTenant, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchChangePassword, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLdapLogin, fetchLogin, fetchLoginMethods, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRbacAdminResetPassword, fetchRbacContactDetail, fetchRbacContacts, fetchRbacCreateRole, fetchRbacCreateUser, fetchRbacDeleteRole, fetchRbacOplog, fetchRbacOplogPost, fetchRbacPermissionList, fetchRbacRoleList, fetchRbacSetForbidden, fetchRbacSettings, fetchRbacUpdateBindings, fetchRbacUpdateSetting, fetchRbacUpdateUserRoles, fetchRbacUserList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchSolutionRoutes, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getHostRoutes, getModule, getModuleRoutePrefix, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, groupByModule, groupSettingsByModule, handleTree, loadAll, mapTree, menuIcons, notify, permissionPrefix, request, resolveApiBase, resolveSiteBase, roleLabel, setupI18n, traverseTreeValues, unloadModule, useAccess, useAccessStore, useAuthStore, usePreferences, useSlotNodes, useSolutionRoutes, useTabKey, useTabsStore, useTenantStore, useUserStore, withSiteBase, external_exports as z };
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" href="/favicon.ico" />
7
7
  <title>React Antd Admin · Shell</title>
8
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-wGgfKOcX0w89VQKxWfhA2A=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
- <script type="importmap" nonce="wGgfKOcX0w89VQKxWfhA2A==">{
8
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-gqN0clQxQgnlBz0xSRc+IA=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
+ <script type="importmap" nonce="gqN0clQxQgnlBz0xSRc+IA==">{
10
10
  "imports": {
11
11
  "react": "/assets/react.js",
12
12
  "react/jsx-runtime": "/assets/jsx-runtime.js",
@@ -130,7 +130,7 @@
130
130
  .boot-skeleton:after{content:"";width:18px;height:18px;margin-left:8px;border:2px solid #d9d9d9;border-top-color:#1677ff;border-radius:50%;animation:bs-spin .8s linear infinite}
131
131
  @keyframes bs-spin{to{transform:rotate(360deg)}}
132
132
  </style>
133
- <script nonce="wGgfKOcX0w89VQKxWfhA2A==" data-ojm-entry="/assets/index-BKtXxIXz.js">(function(){try{var ua=(navigator.userAgent||"").toLowerCase(),p=location.pathname;while(p.length>1&&p.charAt(p.length-1)==="/")p=p.slice(0,-1);if(ua.indexOf("wxwork")>=0&&p.slice(-6)==="/login"&&!new URLSearchParams(location.search).get("wecom_ticket")){var a=(typeof window.__OJ_API_BASE__==="string"&&window.__OJ_API_BASE__.trim())||"/api";while(a.length>1&&a.charAt(a.length-1)==="/")a=a.slice(0,-1);location.replace(a+"/auth/wecom/login");return}}catch(e){}var s=document.createElement("script");s.type="module";s.src=document.currentScript.getAttribute("data-ojm-entry");document.head.appendChild(s);})();</script>
133
+ <script nonce="gqN0clQxQgnlBz0xSRc+IA==" data-ojm-entry="/assets/index-xZhmujjM.js">(function(){try{var ua=(navigator.userAgent||"").toLowerCase(),p=location.pathname;while(p.length>1&&p.charAt(p.length-1)==="/")p=p.slice(0,-1);if(ua.indexOf("wxwork")>=0&&p.slice(-6)==="/login"&&!new URLSearchParams(location.search).get("wecom_ticket")){var a=(typeof window.__OJ_API_BASE__==="string"&&window.__OJ_API_BASE__.trim())||"/api";while(a.length>1&&a.charAt(a.length-1)==="/")a=a.slice(0,-1);location.replace(a+"/auth/wecom/login");return}}catch(e){}var s=document.createElement("script");s.type="module";s.src=document.currentScript.getAttribute("data-ojm-entry");document.head.appendChild(s);})();</script>
134
134
  </head>
135
135
  <body>
136
136
  <div id="root"><div class="boot-skeleton">加载中…</div></div>
@@ -7,9 +7,9 @@
7
7
  "react-router": "7.18.2",
8
8
  "react-router/dom": "7.18.2",
9
9
  "@tanstack/react-query": "5.102.8",
10
- "@oj-module/runtime": "0.2.63",
11
- "@oj-module/runtime/contract": "0.2.63",
12
- "@oj-module/runtime/contract/errors": "0.2.63",
10
+ "@oj-module/runtime": "0.2.64",
11
+ "@oj-module/runtime/contract": "0.2.64",
12
+ "@oj-module/runtime/contract/errors": "0.2.64",
13
13
  "@plane/kit": "0.1.20",
14
14
  "@plane/editor": "0.1.20",
15
15
  "antd": "6.6.1",
@@ -89,6 +89,7 @@ export const useAccess = _fn;
89
89
  export const usePreferences = _fn;
90
90
  // i18n 初始化(宿主链路消费)
91
91
  export const setupI18n = _fn;
92
+ export const ANT_DESIGN_LOCALE = _obj;
92
93
  // 全局副作用(宿主链路消费)
93
94
  export const LayoutEffects = _fn;
94
95
  export const accessControlCodes = _obj;
@@ -6,10 +6,45 @@
6
6
  import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { join } from "node:path";
9
- import { describe, expect, it } from "vitest";
9
+ import { describe, expect, it, vi } from "vitest";
10
10
 
11
11
  import { bootstrapIsolatedSite } from "../bootstrap";
12
12
 
13
+ // 桩 oj 用进程内实现(与 init.test.ts 同套路):bin/oj 是 shebang 脚本,Windows 上
14
+ // execFileSync 无法 spawn(ENOENT 会让能力闸门走「跳过版本校验」分支,被测形态失真)。
15
+ // 且 buildBackend 自接入 mergeConfigForDev 后要供给 dev 密钥,桩必须带 secret 能力:
16
+ // keygen 落一对 PEM、seal 回 ENC[...]、open 恒成功(与真实 oj 的退出码语义一致)。
17
+ vi.mock("node:child_process", async (importOriginal) => {
18
+ const actual = await importOriginal<typeof import("node:child_process")>();
19
+ const mfs = await import("node:fs");
20
+ const mpath = await import("node:path");
21
+ const execFileSync = (file: string, args: readonly string[]): string => {
22
+ // 真实 spawn 的语义:文件不存在即 ENOENT(「没装 oj」用例依赖这条)
23
+ if (!mfs.existsSync(String(file)))
24
+ throw new Error(`spawnSync ${file} ENOENT`);
25
+ const a = [...args];
26
+ if (a[0] === "-V")
27
+ return "oj 0.1.85";
28
+ if (a[0] === "build")
29
+ return a.includes("--help") ? " --module-root <MODULE_ROOT> 源码树根" : "";
30
+ if (a[0] === "secret") {
31
+ if (a[1] === "keygen") {
32
+ const keysDir = a[a.indexOf("--out-dir") + 1]!;
33
+ mfs.mkdirSync(keysDir, { recursive: true });
34
+ mfs.writeFileSync(mpath.join(keysDir, "secrets-private.pem"), "PRIV\n");
35
+ mfs.writeFileSync(mpath.join(keysDir, "secrets-public.pem"), "PUB\n");
36
+ return "";
37
+ }
38
+ if (a[1] === "seal")
39
+ return "ENC[TESTSEALED]";
40
+ if (a[1] === "open")
41
+ return "TESTPLAIN";
42
+ }
43
+ return "";
44
+ };
45
+ return { ...actual, execFileSync };
46
+ });
47
+
13
48
  /** 假 oj 进程:不起真进程,ready 立即就绪(与 dev.ts 的 ojStarter 注入同形态) */
14
49
  function fakeStarter() {
15
50
  const started: Array<{ configPath: string, base: string, apiSrc: string }> = [];
@@ -2,5 +2,20 @@
2
2
  "menu": {
3
3
  "personalCenter": "Personal Center",
4
4
  "profile": "My Profile"
5
+ },
6
+ "profile": {
7
+ "title": "My Profile",
8
+ "avatar": "Avatar",
9
+ "avatarRequired": "Please upload an avatar!",
10
+ "username": "Username",
11
+ "usernameRequired": "Please enter your username!",
12
+ "email": "Email",
13
+ "emailRequired": "Please enter your email!",
14
+ "phoneNumber": "Phone",
15
+ "phoneNumberRequired": "Please enter your phone number!",
16
+ "description": "Bio",
17
+ "descriptionPlaceholder": "Bio",
18
+ "updated": "Profile updated",
19
+ "updateFailed": "Failed to update profile: {{message}}"
5
20
  }
6
21
  }
@@ -2,5 +2,20 @@
2
2
  "menu": {
3
3
  "personalCenter": "个人中心",
4
4
  "profile": "我的资料"
5
+ },
6
+ "profile": {
7
+ "title": "我的资料",
8
+ "avatar": "头像",
9
+ "avatarRequired": "请上传头像!",
10
+ "username": "用户名",
11
+ "usernameRequired": "请输入您的用户名!",
12
+ "email": "邮箱",
13
+ "emailRequired": "请输入您的邮箱!",
14
+ "phoneNumber": "联系电话",
15
+ "phoneNumberRequired": "请输入您的联系电话!",
16
+ "description": "个人简介",
17
+ "descriptionPlaceholder": "个人简介",
18
+ "updated": "更新基本信息成功",
19
+ "updateFailed": "更新基本信息失败:{{message}}"
5
20
  }
6
21
  }
@@ -6,10 +6,12 @@ import {
6
6
  } from "@ant-design/pro-components";
7
7
  import { BasicContent, FormAvatarItem, useUserStore } from "@oj-module/runtime";
8
8
  import { App, Form, Input } from "antd";
9
+ import { useTranslation } from "react-i18next";
9
10
 
10
11
  import { personalCenterProfilePost } from "../../__client";
11
12
 
12
13
  export default function Profile() {
14
+ const { t } = useTranslation();
13
15
  const currentUser = useUserStore();
14
16
  const { message } = App.useApp();
15
17
  const getAvatarURL = () => {
@@ -39,16 +41,16 @@ export default function Profile() {
39
41
  description: values.description,
40
42
  });
41
43
  await useUserStore.getState().getUserInfo();
42
- message.success("更新基本信息成功");
44
+ message.success(t("personal-center:profile.updated"));
43
45
  }
44
46
  catch (e) {
45
- message.error(`更新基本信息失败:${e instanceof Error ? e.message : String(e)}`);
47
+ message.error(t("personal-center:profile.updateFailed", { message: e instanceof Error ? e.message : String(e) }));
46
48
  }
47
49
  };
48
50
 
49
51
  return (
50
52
  <BasicContent className="max-w-md ml-10">
51
- <h3>我的资料</h3>
53
+ <h3>{t("personal-center:profile.title")}</h3>
52
54
  <ProForm
53
55
  layout="vertical"
54
56
  onFinish={handleFinish}
@@ -60,11 +62,11 @@ export default function Profile() {
60
62
  >
61
63
  <Form.Item
62
64
  name="avatar"
63
- label="头像"
65
+ label={t("personal-center:profile.avatar")}
64
66
  rules={[
65
67
  {
66
68
  required: true,
67
- message: "请输入您的昵称!",
69
+ message: t("personal-center:profile.avatarRequired"),
68
70
  },
69
71
  ]}
70
72
  >
@@ -72,31 +74,31 @@ export default function Profile() {
72
74
  </Form.Item>
73
75
  <ProFormText
74
76
  name="username"
75
- label="用户名"
77
+ label={t("personal-center:profile.username")}
76
78
  rules={[
77
79
  {
78
80
  required: true,
79
- message: "请输入您的用户名!",
81
+ message: t("personal-center:profile.usernameRequired"),
80
82
  },
81
83
  ]}
82
84
  />
83
85
  <ProFormText
84
86
  name="email"
85
- label="邮箱"
87
+ label={t("personal-center:profile.email")}
86
88
  rules={[
87
89
  {
88
90
  required: true,
89
- message: "请输入您的邮箱!",
91
+ message: t("personal-center:profile.emailRequired"),
90
92
  },
91
93
  ]}
92
94
  />
93
95
  <ProFormDigit
94
96
  name="phoneNumber"
95
- label="联系电话"
97
+ label={t("personal-center:profile.phoneNumber")}
96
98
  rules={[
97
99
  {
98
100
  required: true,
99
- message: "请输入您的联系电话!",
101
+ message: t("personal-center:profile.phoneNumberRequired"),
100
102
  },
101
103
  ]}
102
104
  >
@@ -105,8 +107,8 @@ export default function Profile() {
105
107
  <ProFormTextArea
106
108
  allowClear
107
109
  name="description"
108
- label="个人简介"
109
- placeholder="个人简介"
110
+ label={t("personal-center:profile.description")}
111
+ placeholder={t("personal-center:profile.descriptionPlaceholder")}
110
112
  />
111
113
  </ProForm>
112
114
  </BasicContent>
@@ -40,12 +40,15 @@
40
40
  "departments": "Departments"
41
41
  },
42
42
  "mainDept": "Main dept",
43
- "filterDept": "Filter by department (incl. sub-depts)"
43
+ "filterDept": "Filter by department (incl. sub-depts)",
44
+ "password": "Password",
45
+ "usernameHint": "ldap:/ sso:/ wecom: prefixes are reserved for external identity sources"
44
46
  },
45
47
  "userRoles": {
46
48
  "title": "Membership",
47
49
  "saved": "Roles updated",
48
- "none": "(no roles)"
50
+ "none": "(no roles)",
51
+ "deprecated": " (deprecated)"
49
52
  },
50
53
  "rolesPage": {
51
54
  "tabRoles": "Roles",
@@ -55,7 +58,18 @@
55
58
  "holders": "Holders",
56
59
  "permissions": "Bound Permissions",
57
60
  "titleKey": "Title Key",
58
- "readonlyHint": "Read-only view: roles and bindings are managed by solution tooling (upsertSolutionPerms)"
61
+ "readonlyHint": "Read-only view: roles and bindings are managed by solution tooling (upsertSolutionPerms)",
62
+ "columns": {
63
+ "module": "Module",
64
+ "title": "Title",
65
+ "code": "Code"
66
+ },
67
+ "roleTitle": {
68
+ "superadmin": "Super Admin",
69
+ "admin": "Administrator",
70
+ "editor": "Editor",
71
+ "viewer": "Viewer"
72
+ }
59
73
  },
60
74
  "departments": {
61
75
  "create": "New Department",
@@ -256,7 +270,14 @@
256
270
  }
257
271
  },
258
272
  "setting": {
273
+ "group": {
274
+ "general": "General",
275
+ "security": "Security",
276
+ "login": "Login Methods",
277
+ "ldap": "LDAP"
278
+ },
259
279
  "site_title": "Site Title",
280
+ "market_cdn_url": "Market CDN URL",
260
281
  "ldap_enabled": "Enable LDAP login",
261
282
  "sso_enabled": "Enable SSO login",
262
283
  "wecom_enabled": "Enable WeCom login",
@@ -40,12 +40,15 @@
40
40
  "departments": "所属部门"
41
41
  },
42
42
  "mainDept": "主部门",
43
- "filterDept": "按部门筛选(含子部门)"
43
+ "filterDept": "按部门筛选(含子部门)",
44
+ "password": "口令",
45
+ "usernameHint": "ldap:/ sso:/ wecom: 为外部身份源保留"
44
46
  },
45
47
  "userRoles": {
46
48
  "title": "成员资格",
47
49
  "saved": "角色已更新",
48
- "none": "(无角色)"
50
+ "none": "(无角色)",
51
+ "deprecated": "(已废弃)"
49
52
  },
50
53
  "rolesPage": {
51
54
  "tabRoles": "角色",
@@ -55,7 +58,18 @@
55
58
  "holders": "持有者",
56
59
  "permissions": "已绑定权限点",
57
60
  "titleKey": "标题键",
58
- "readonlyHint": "只读视图:角色与绑定由解决方案工具链(upsertSolutionPerms)管理"
61
+ "readonlyHint": "只读视图:角色与绑定由解决方案工具链(upsertSolutionPerms)管理",
62
+ "columns": {
63
+ "module": "模块",
64
+ "title": "标题",
65
+ "code": "编码"
66
+ },
67
+ "roleTitle": {
68
+ "superadmin": "超级管理员",
69
+ "admin": "系统管理员",
70
+ "editor": "编辑",
71
+ "viewer": "访客"
72
+ }
59
73
  },
60
74
  "departments": {
61
75
  "create": "新建部门",
@@ -256,7 +270,14 @@
256
270
  }
257
271
  },
258
272
  "setting": {
273
+ "group": {
274
+ "general": "常规",
275
+ "security": "安全",
276
+ "login": "登录方式",
277
+ "ldap": "LDAP"
278
+ },
259
279
  "site_title": "站点标题",
280
+ "market_cdn_url": "市场 CDN 地址",
260
281
  "ldap_enabled": "启用 LDAP 登录",
261
282
  "sso_enabled": "启用 SSO 登录",
262
283
  "wecom_enabled": "启用企业微信登录",
@@ -37,13 +37,20 @@ export default function RolesPage() {
37
37
  </Space>
38
38
  ),
39
39
  },
40
- { title: "module", dataIndex: "module" },
40
+ { title: t("system:rolesPage.columns.module"), dataIndex: "module" },
41
41
  {
42
42
  // R2:solution 角色带 title_key → 按当前语言实时翻译,title(安装期解析文本)兜底;
43
- // custom/builtin 无 key 原样显示 title(对齐下方权限点 title_key 的渲染先例)
44
- title: "title",
43
+ // 内置角色(builtin)的 title 是种子里的站点默认语言文本,按角色名再兜一层 locale;
44
+ // custom 角色无 key 原样显示 title(对齐下方权限点 title_key 的渲染先例)
45
+ title: t("system:rolesPage.columns.title"),
45
46
  dataIndex: "title",
46
- render: (_: string, row: RoleRow) => (row.title_key ? t(row.title_key, { defaultValue: row.title ?? "" }) : row.title),
47
+ render: (_: string, row: RoleRow) => {
48
+ if (row.title_key)
49
+ return t(row.title_key, { defaultValue: row.title ?? "" });
50
+ return row.builtin
51
+ ? t(`system:rolesPage.roleTitle.${row.name}`, { defaultValue: row.title ?? row.name })
52
+ : row.title;
53
+ },
47
54
  },
48
55
  { title: t("system:rolesPage.holders"), dataIndex: "holders" },
49
56
  {
@@ -55,11 +62,11 @@ export default function RolesPage() {
55
62
 
56
63
  const permColumns = [
57
64
  {
58
- title: "code",
65
+ title: t("system:rolesPage.columns.code"),
59
66
  dataIndex: "code",
60
67
  render: (code: string, row: PermRow) => (row.deprecated ? <Tag color="default">{code}</Tag> : <span>{code}</span>),
61
68
  },
62
- { title: "module", dataIndex: "module" },
69
+ { title: t("system:rolesPage.columns.module"), dataIndex: "module" },
63
70
  {
64
71
  // title_key 是跨模块 namespace(如 wiki:perm.*),本模块 locale 不命中回退 code(R6)
65
72
  title: t("system:rolesPage.titleKey"),
@@ -93,7 +93,7 @@ export default function SettingsPage() {
93
93
  return (
94
94
  <Space direction="vertical" style={{ width: "100%", paddingTop: 16 }}>
95
95
  {groups.map(g => (
96
- <Card key={g.group} title={g.group}>
96
+ <Card key={g.group} title={t(`system:setting.group.${g.group}`, { defaultValue: g.group })}>
97
97
  <Space direction="vertical" style={{ width: "100%" }}>
98
98
  {g.items.map(item => (
99
99
  <SettingRow key={item.key} item={item} t={t} onSave={(key, value) => putMut.mutate({ key, value })} />
@@ -188,10 +188,10 @@ export default function UsersPage() {
188
188
  onOk={() => createForm.submit()}
189
189
  >
190
190
  <Form form={createForm} layout="vertical" onFinish={values => createMut.mutate(values)}>
191
- <Form.Item name="username" label={t("system:users.columns.username")} rules={[{ required: true }]} extra="ldap:/ sso:/ wecom: 为外部身份源保留">
191
+ <Form.Item name="username" label={t("system:users.columns.username")} rules={[{ required: true }]} extra={t("system:users.usernameHint")}>
192
192
  <Input />
193
193
  </Form.Item>
194
- <Form.Item name="password" label="Password" rules={[{ required: true }, { min: 6 }]} extra={t("system:users.pwdPlaceholder")}>
194
+ <Form.Item name="password" label={t("system:users.password")} rules={[{ required: true }, { min: 6 }]} extra={t("system:users.pwdPlaceholder")}>
195
195
  <Input.Password />
196
196
  </Form.Item>
197
197
  <Form.Item name="displayName" label={t("system:users.columns.displayName")}>
@@ -211,7 +211,7 @@ export default function UsersPage() {
211
211
  onOk={() => pwdForm.submit()}
212
212
  >
213
213
  <Form form={pwdForm} layout="vertical" onFinish={values => pwdTarget && pwdMut.mutate({ uid: pwdTarget.id, password: values.password })}>
214
- <Form.Item name="password" label="Password" rules={[{ required: true }, { min: 6 }]} extra={t("system:users.pwdPlaceholder")}>
214
+ <Form.Item name="password" label={t("system:users.password")} rules={[{ required: true }, { min: 6 }]} extra={t("system:users.pwdPlaceholder")}>
215
215
  <Input.Password />
216
216
  </Form.Item>
217
217
  </Form>
@@ -58,7 +58,7 @@ export default function UserRolesPage() {
58
58
  <Space direction="vertical">
59
59
  <Checkbox.Group
60
60
  options={(rolesQuery.data ?? []).map(r => ({
61
- label: `${r.name}${r.deprecated ? "(已废弃)" : ""}`,
61
+ label: `${r.name}${r.deprecated ? t("system:userRoles.deprecated") : ""}`,
62
62
  value: r.name,
63
63
  // 非 superadmin 授不了平台级角色:置灰,提交后 403 太晚也太含糊
64
64
  disabled: r.name === "superadmin" && !isSuper,
@@ -45,7 +45,20 @@
45
45
  "installing": "Installing",
46
46
  "uninstalling": "Uninstalling",
47
47
  "progress": "Progress",
48
- "stage": "Stage",
48
+ "stage": {
49
+ "preflight": "Preflight",
50
+ "downloading": "Downloading",
51
+ "verifying": "Verifying",
52
+ "extracting": "Extracting",
53
+ "migrating": "Migrating data",
54
+ "lock_commit": "Committing version lock",
55
+ "registering": "Registering module",
56
+ "deploying_web": "Deploying web assets",
57
+ "reloading": "Reloading config",
58
+ "smoke": "Smoke check",
59
+ "audit": "Final audit",
60
+ "done": "Done"
61
+ },
49
62
  "status": {
50
63
  "running": "Running",
51
64
  "done": "Done",
@@ -45,7 +45,20 @@
45
45
  "installing": "安装中",
46
46
  "uninstalling": "卸载中",
47
47
  "progress": "进度",
48
- "stage": "阶段",
48
+ "stage": {
49
+ "preflight": "预检",
50
+ "downloading": "下载中",
51
+ "verifying": "校验中",
52
+ "extracting": "解包中",
53
+ "migrating": "数据迁移",
54
+ "lock_commit": "提交版本锁",
55
+ "registering": "注册模块",
56
+ "deploying_web": "部署前端",
57
+ "reloading": "重载配置",
58
+ "smoke": "冒烟探活",
59
+ "audit": "终态审计",
60
+ "done": "完成"
61
+ },
49
62
  "status": {
50
63
  "running": "进行中",
51
64
  "done": "完成",
@@ -8,9 +8,9 @@
8
8
  "react-router": "7.18.2",
9
9
  "react-router/dom": "7.18.2",
10
10
  "@tanstack/react-query": "5.102.8",
11
- "@oj-module/runtime": "0.2.63",
12
- "@oj-module/runtime/contract": "0.2.63",
13
- "@oj-module/runtime/contract/errors": "0.2.63",
11
+ "@oj-module/runtime": "0.2.64",
12
+ "@oj-module/runtime/contract": "0.2.64",
13
+ "@oj-module/runtime/contract/errors": "0.2.64",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",