cabloy 5.1.44 → 5.1.46

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 (30) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +1 -1
  3. package/vona/packages-cli/cli/package.json +1 -1
  4. package/vona/packages-cli/cli-set-api/package.json +1 -1
  5. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.bean.ts +12 -0
  6. package/vona/packages-vona/vona/package.json +1 -1
  7. package/vona/src/suite/a-demo/modules/demo-basic/src/.metadata/index.ts +48 -2
  8. package/vona/src/suite/a-demo/modules/demo-basic/src/.metadata/locales.ts +18 -0
  9. package/vona/src/suite/a-demo/modules/demo-basic/src/bean/ssrMenu.admin.ts +0 -1
  10. package/vona/src/suite/a-demo/modules/demo-basic/src/bean/ssrMenu.demo.ts +36 -0
  11. package/vona/src/suite/a-demo/modules/demo-basic/src/bean/ssrMenuGroup.demo.ts +20 -0
  12. package/vona/src/suite/a-demo/modules/demo-basic/src/config/locale/en-us.ts +3 -0
  13. package/vona/src/suite/a-demo/modules/demo-basic/src/config/locale/zh-cn.ts +3 -0
  14. package/vona/src/suite/a-demo/modules/demo-basic/src/index.ts +1 -0
  15. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrMenuGroupWeb/boilerplate/{{sceneName}}.{{beanName}}.ts_ +18 -0
  16. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrMenuWeb/boilerplate/{{sceneName}}.{{beanName}}.ts_ +19 -0
  17. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +5 -3
  18. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +6 -0
  19. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  20. package/vona/src/suite-vendor/a-vona/modules/a-executor/package.json +1 -1
  21. package/vona/src/suite-vendor/a-vona/modules/a-executor/src/service/executor.ts +4 -1
  22. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  23. package/zova/src/suite/a-demo/modules/demo-basic/src/routes.ts +8 -6
  24. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.menu.tsx +1 -1
  25. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.tabs.tsx +1 -1
  26. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/menu.ts +34 -20
  27. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.tabs.tsx +13 -12
  28. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/menu.ts +34 -20
  29. /package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/{ssrMenu → ssrMenuAdmin}/boilerplate/{{sceneName}}.{{beanName}}.ts_ +0 -0
  30. /package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/{ssrMenuGroup → ssrMenuGroupAdmin}/boilerplate/{{sceneName}}.{{beanName}}.ts_ +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.46
4
+
5
+ ### Improvements
6
+
7
+ - Refine SSR site base handling in `beanSsrSiteBase.ts`.
8
+ - Improve executor behavior in `executor.ts`.
9
+ - Update tab rendering logic in `render.tabs.tsx`.
10
+ - Adjust route configuration in `routes.ts`.
11
+
12
+ ## 5.1.45
13
+
14
+ ### Features
15
+
16
+ - Add SSR menu boilerplates for demo applications.
17
+ - Add SSR menu boilerplates for web applications.
18
+ - Add SSR menu boilerplates for admin applications.
19
+
20
+ ### Bug Fixes
21
+
22
+ - Remove redundant locale menu refetch watchers.
23
+ - Improve menu query key scoping and prevent circular group recursion.
24
+
3
25
  ## 5.1.44
4
26
 
5
27
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.44",
3
+ "version": "5.1.46",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli",
3
- "version": "1.1.104",
3
+ "version": "1.1.105",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.102",
3
+ "version": "1.1.103",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
@@ -20,9 +20,12 @@ declare module '@cabloy/cli' {
20
20
  moduleResourceName: string;
21
21
  boilerplate: string;
22
22
  ssrSiteModuleName: string;
23
+ ssrSiteModuleNameWeb: string;
23
24
  ssrSiteOnionName: string;
25
+ ssrSiteOnionNameWeb: string;
24
26
  ssrSiteGroupName: string;
25
27
  ssrSiteRestNpm: string;
28
+ ssrSiteRestNpmWeb: string;
26
29
  }
27
30
  }
28
31
 
@@ -37,15 +40,24 @@ export class CliCreateBean extends BeanCliBase {
37
40
  argv.ssrSiteModuleName = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
38
41
  ? 'vona-module-start-siteadmin'
39
42
  : 'vona-module-basic-siteadmin';
43
+ argv.ssrSiteModuleNameWeb = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
44
+ ? 'vona-module-start-siteweb'
45
+ : 'vona-module-basic-siteweb';
40
46
  argv.ssrSiteOnionName = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
41
47
  ? 'start-siteadmin:admin'
42
48
  : 'basic-siteadmin:admin';
49
+ argv.ssrSiteOnionNameWeb = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
50
+ ? 'start-siteweb:web'
51
+ : 'basic-siteweb:web';
43
52
  argv.ssrSiteGroupName = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
44
53
  ? 'start-siteadmin:management'
45
54
  : 'basic-siteadmin:management';
46
55
  argv.ssrSiteRestNpm = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
47
56
  ? 'zova-rest-cabloy-start-admin'
48
57
  : 'zova-rest-cabloy-basic-admin';
58
+ argv.ssrSiteRestNpmWeb = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start'))
59
+ ? 'zova-rest-cabloy-start-web'
60
+ : 'zova-rest-cabloy-basic-web';
49
61
  // module name/info
50
62
  const moduleName = argv.module;
51
63
  argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.39",
3
+ "version": "5.1.40",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
6
6
  "keywords": [
@@ -1,12 +1,15 @@
1
1
  // eslint-disable
2
2
  /** ssrMenu: begin */
3
3
  export * from '../bean/ssrMenu.admin.ts';
4
+ export * from '../bean/ssrMenu.demo.ts';
4
5
  import type { ISsrMenuOptionsAdmin } from '../bean/ssrMenu.admin.ts';
6
+ import type { ISsrMenuOptionsDemo } from '../bean/ssrMenu.demo.ts';
5
7
  import 'vona-module-a-ssr';
6
8
  declare module 'vona-module-a-ssr' {
7
9
 
8
10
  export interface ISsrMenuRecord {
9
11
  'demo-basic:admin': ISsrMenuOptionsAdmin;
12
+ 'demo-basic:demo': ISsrMenuOptionsDemo;
10
13
  }
11
14
 
12
15
 
@@ -22,11 +25,51 @@ declare module 'vona-module-demo-basic' {
22
25
  get $beanFullName(): 'demo-basic.ssrMenu.admin';
23
26
  get $onionName(): 'demo-basic:admin';
24
27
  get $onionOptions(): ISsrMenuOptionsAdmin;
28
+ }
29
+
30
+ export interface SsrMenuDemo {
31
+ /** @internal */
32
+ get scope(): ScopeModuleDemoBasic;
33
+ }
34
+
35
+ export interface SsrMenuDemo {
36
+ get $beanFullName(): 'demo-basic.ssrMenu.demo';
37
+ get $onionName(): 'demo-basic:demo';
38
+ get $onionOptions(): ISsrMenuOptionsDemo;
25
39
  }
26
40
  }
27
41
  /** ssrMenu: end */
42
+ /** ssrMenuGroup: begin */
43
+ export * from '../bean/ssrMenuGroup.demo.ts';
44
+ import type { ISsrMenuGroupOptionsDemo } from '../bean/ssrMenuGroup.demo.ts';
45
+ import 'vona-module-a-ssr';
46
+ declare module 'vona-module-a-ssr' {
47
+
48
+ export interface ISsrMenuGroupRecord {
49
+ 'demo-basic:demo': ISsrMenuGroupOptionsDemo;
50
+ }
51
+
52
+
53
+ }
54
+ declare module 'vona-module-demo-basic' {
55
+
56
+ export interface SsrMenuGroupDemo {
57
+ /** @internal */
58
+ get scope(): ScopeModuleDemoBasic;
59
+ }
60
+
61
+ export interface SsrMenuGroupDemo {
62
+ get $beanFullName(): 'demo-basic.ssrMenuGroup.demo';
63
+ get $onionName(): 'demo-basic:demo';
64
+ get $onionOptions(): ISsrMenuGroupOptionsDemo;
65
+ }
66
+ }
67
+ /** ssrMenuGroup: end */
68
+ /** locale: begin */
69
+ import { locales } from './locales.ts';
70
+ /** locale: end */
28
71
  /** scope: begin */
29
- import { BeanScopeBase, type BeanScopeUtil } from 'vona';
72
+ import { BeanScopeBase, type BeanScopeUtil, type TypeModuleLocales, type TypeLocaleBase } from 'vona';
30
73
  import { Scope } from 'vona-module-a-bean';
31
74
 
32
75
  @Scope()
@@ -34,6 +77,7 @@ export class ScopeModuleDemoBasic extends BeanScopeBase {}
34
77
 
35
78
  export interface ScopeModuleDemoBasic {
36
79
  util: BeanScopeUtil;
80
+ locale: TypeModuleLocales<(typeof locales)[TypeLocaleBase]>;
37
81
  }
38
82
 
39
83
  import 'vona';
@@ -48,7 +92,9 @@ declare module 'vona' {
48
92
 
49
93
 
50
94
 
51
-
95
+ export interface IBeanScopeLocale {
96
+ 'demo-basic': (typeof locales)[TypeLocaleBase];
97
+ }
52
98
 
53
99
 
54
100
  }
@@ -0,0 +1,18 @@
1
+ import type { TypeLocaleBase } from 'vona';
2
+
3
+ import { $makeLocaleMagic } from 'vona';
4
+
5
+ import locale_en_us from '../config/locale/en-us.ts';
6
+ import locale_zh_cn from '../config/locale/zh-cn.ts';
7
+
8
+ export const locales = {
9
+ 'en-us': locale_en_us,
10
+ 'zh-cn': locale_zh_cn,
11
+ };
12
+
13
+ export function $locale<K extends keyof (typeof locales)[TypeLocaleBase]>(
14
+ key: K,
15
+ ...args: any[]
16
+ ): any {
17
+ return $makeLocaleMagic(`demo-basic::${key}`, ...args);
18
+ }
@@ -15,7 +15,6 @@ export interface ISsrMenuOptionsAdmin extends IDecoratorSsrMenuOptions<ISsrSiteO
15
15
  link: 'http://localhost:7102/admin' as any,
16
16
  external: true,
17
17
  target: '_self',
18
- group: undefined,
19
18
  },
20
19
  site: ['basic-siteweb:web'],
21
20
  })
@@ -0,0 +1,36 @@
1
+ import type { IDecoratorSsrMenuOptions } from 'vona-module-a-ssr';
2
+ import type { ISsrSiteOptionsWeb } from 'vona-module-basic-siteweb';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { $order } from 'vona-module-a-openapiutils';
6
+ import { SsrMenu } from 'vona-module-a-ssr';
7
+
8
+ export interface ISsrMenuOptionsDemo extends IDecoratorSsrMenuOptions<ISsrSiteOptionsWeb> {}
9
+
10
+ @SsrMenu<ISsrMenuOptionsDemo>({
11
+ items: {
12
+ state: {
13
+ title: 'State',
14
+ order: $order(1),
15
+ icon: undefined,
16
+ link: '/demo/basic/state',
17
+ group: 'demo-basic:demo',
18
+ },
19
+ component: {
20
+ title: 'Component',
21
+ order: $order(2),
22
+ icon: undefined,
23
+ link: '/demo/basic/component',
24
+ group: 'demo-basic:demo',
25
+ },
26
+ cssInJs: {
27
+ title: 'CSS-in-JS',
28
+ order: $order(3),
29
+ icon: undefined,
30
+ link: '/demo/basic/style',
31
+ group: 'demo-basic:demo',
32
+ },
33
+ },
34
+ site: ['basic-siteweb:web'],
35
+ })
36
+ export class SsrMenuDemo extends BeanBase {}
@@ -0,0 +1,20 @@
1
+ import type { IDecoratorSsrMenuGroupOptions } from 'vona-module-a-ssr';
2
+ import type { ISsrSiteOptionsWeb } from 'vona-module-basic-siteweb';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { $order } from 'vona-module-a-openapiutils';
6
+ import { SsrMenuGroup } from 'vona-module-a-ssr';
7
+
8
+ import { $locale } from '../.metadata/locales.ts';
9
+
10
+ export interface ISsrMenuGroupOptionsDemo extends IDecoratorSsrMenuGroupOptions<ISsrSiteOptionsWeb> {}
11
+
12
+ @SsrMenuGroup<ISsrMenuGroupOptionsDemo>({
13
+ item: {
14
+ title: $locale('Demo'),
15
+ order: $order(2),
16
+ icon: ':editor:code-block',
17
+ },
18
+ site: ['basic-siteweb:web'],
19
+ })
20
+ export class SsrMenuGroupDemo extends BeanBase {}
@@ -0,0 +1,3 @@
1
+ export default {
2
+ Demo: 'Demo',
3
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ Demo: '演示',
3
+ };
@@ -1 +1,2 @@
1
+ export * from './.metadata/locales.ts';
1
2
  export * from './.metadata/index.ts';
@@ -0,0 +1,18 @@
1
+ import type { IDecoratorSsrMenuGroupOptions } from 'vona-module-a-ssr';
2
+ import type { ISsrSiteOptionsWeb } from '<%=argv.ssrSiteModuleNameWeb%>';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { $order } from 'vona-module-a-openapiutils';
6
+ import { SsrMenuGroup } from 'vona-module-a-ssr';
7
+
8
+ export interface ISsrMenuGroupOptions<%=argv.beanNameCapitalize%> extends IDecoratorSsrMenuGroupOptions<ISsrSiteOptionsWeb> {}
9
+
10
+ @SsrMenuGroup<ISsrMenuGroupOptions<%=argv.beanNameCapitalize%>>({
11
+ item: {
12
+ title: '<%=argv.beanNameCapitalize%>',
13
+ order: $order(2),
14
+ icon: undefined,
15
+ },
16
+ site: ['<%=argv.ssrSiteOnionNameWeb%>'],
17
+ })
18
+ export class SsrMenuGroup<%=argv.beanNameCapitalize%> extends BeanBase {}
@@ -0,0 +1,19 @@
1
+ import type { IDecoratorSsrMenuOptions } from 'vona-module-a-ssr';
2
+ import type { ISsrSiteOptionsWeb } from '<%=argv.ssrSiteModuleNameWeb%>';
3
+
4
+ import { BeanBase } from 'vona';
5
+ import { $order } from 'vona-module-a-openapiutils';
6
+ import { SsrMenu } from 'vona-module-a-ssr';
7
+
8
+ export interface ISsrMenuOptions<%=argv.beanNameCapitalize%> extends IDecoratorSsrMenuOptions<ISsrSiteOptionsWeb> {}
9
+
10
+ @SsrMenu<ISsrMenuOptions<%=argv.beanNameCapitalize%>>({
11
+ item: {
12
+ title: '<%=argv.beanNameCapitalize%>',
13
+ order: $order(1),
14
+ icon: undefined,
15
+ link: undefined,
16
+ },
17
+ site: ['<%=argv.ssrSiteOnionNameWeb%>'],
18
+ })
19
+ export class SsrMenu<%=argv.beanNameCapitalize%> extends BeanBase {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-ssr",
3
- "version": "5.1.12",
3
+ "version": "5.1.14",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -63,12 +63,14 @@
63
63
  "ssrMenu": {
64
64
  "optionsGlobalInterfaceName": "IDecoratorSsrMenuOptions",
65
65
  "optionsGlobalInterfaceFrom": "vona-module-a-ssr",
66
- "boilerplate": "ssrMenu/boilerplate"
66
+ "boilerplate": "ssrMenuAdmin/boilerplate",
67
+ "boilerplateWeb": "ssrMenuWeb/boilerplate"
67
68
  },
68
69
  "ssrMenuGroup": {
69
70
  "optionsGlobalInterfaceName": "IDecoratorSsrMenuGroupOptions",
70
71
  "optionsGlobalInterfaceFrom": "vona-module-a-ssr",
71
- "boilerplate": "ssrMenuGroup/boilerplate"
72
+ "boilerplate": "ssrMenuGroupAdmin/boilerplate",
73
+ "boilerplateWeb": "ssrMenuGroupWeb/boilerplate"
72
74
  }
73
75
  },
74
76
  "capabilities": {
@@ -209,6 +209,12 @@ export class BeanSsrSiteBase<
209
209
  headers.accept = headers.Accept;
210
210
  delete headers.Accept;
211
211
  }
212
+ for (const headerKey of ['x-vona-locale', 'x-vona-tz']) {
213
+ const headerValue = headers[headerKey];
214
+ if (headerValue !== undefined) {
215
+ this.ctx.request.headers[headerKey] = headerValue;
216
+ }
217
+ }
212
218
  // passport
213
219
  if (!ctxInited) {
214
220
  // init instance
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.26",
3
+ "version": "5.1.28",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-executor",
3
- "version": "5.1.5",
3
+ "version": "5.1.6",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -72,7 +72,10 @@ export class ServiceExecutor extends BeanBase {
72
72
  }
73
73
  }
74
74
  },
75
- { innerAccess: options?.innerAccess, extraData: options?.extraData },
75
+ {
76
+ innerAccess: options?.innerAccess,
77
+ extraData: options?.extraData,
78
+ },
76
79
  );
77
80
  }
78
81
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.32",
3
+ "version": "5.1.33",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -11,24 +11,26 @@ import { ZPageToolOne } from './.metadata/page/toolOne.js';
11
11
  import { ZPageToolTwo } from './.metadata/page/toolTwo.js';
12
12
 
13
13
  export const routes: IModuleRoute[] = [
14
- { path: 'state', component: ZPageState },
15
- { path: 'component', component: ZPageComponent },
16
- { path: 'locale', component: ZPageLocale },
17
- { path: 'style', component: ZPageStyle },
18
- { path: 'routeQuery', component: ZPageRouteQuery },
14
+ { path: 'state', component: ZPageState, meta: { requiresAuth: false } },
15
+ { path: 'component', component: ZPageComponent, meta: { requiresAuth: false } },
16
+ { path: 'locale', component: ZPageLocale, meta: { requiresAuth: false } },
17
+ { path: 'style', component: ZPageStyle, meta: { requiresAuth: false } },
18
+ { path: 'routeQuery', component: ZPageRouteQuery, meta: { requiresAuth: false } },
19
19
  {
20
20
  name: 'routeParams',
21
21
  path: 'routeParams/:id?',
22
22
  component: ZPageRouteParams,
23
23
  meta: {
24
24
  componentKeyMode: 'nameOnly',
25
+ requiresAuth: false,
25
26
  },
26
27
  },
27
- { path: 'routeQueryB', component: ZPageRouteQueryB },
28
+ { path: 'routeQueryB', component: ZPageRouteQueryB, meta: { requiresAuth: false } },
28
29
  {
29
30
  name: 'toolOne',
30
31
  path: 'toolOne/:id?',
31
32
  component: ZPageToolOne,
33
+ meta: { requiresAuth: false },
32
34
  },
33
35
  {
34
36
  name: 'toolTwo',
@@ -9,7 +9,7 @@ import { TypeMenuItem, TypeMenuTree } from '../../.metadata/index.js';
9
9
  @Render()
10
10
  export class RenderMenu extends BeanRenderBase {
11
11
  _renderMenuItem(item: TypeMenuItem) {
12
- const titleLocale = this.$text(item.title ?? '');
12
+ const titleLocale = item.title ?? '';
13
13
  if (item.folder) {
14
14
  return (
15
15
  <li>
@@ -29,7 +29,7 @@ export class RenderTabs extends BeanRenderBase {
29
29
  const $$modelTabs = this.$$modelTabs;
30
30
  const { tabKey, info } = tab;
31
31
  const className = tabKey === $$modelTabs.tabKeyCurrent ? 'tab-active text-primary' : '';
32
- const titleLocale = this.$text(info?.title || '');
32
+ const titleLocale = info?.title || '';
33
33
  const tabIcon = this.getTabIcon(tab);
34
34
  return (
35
35
  <a
@@ -28,19 +28,10 @@ export class ModelMenu extends BeanModelBase {
28
28
  // event
29
29
  if (process.env.CLIENT && this.sys.config.ssr.hmr) {
30
30
  this._eventSsrHmrReload = this.sys.meta.event.on('a-ssrhmr:reload', async (_data, next) => {
31
- await this.$refetchQueries({ queryKey: ['retrieveMenus'] });
31
+ await this._refetchRetrieveMenus();
32
32
  return next();
33
33
  });
34
34
  }
35
- // locale
36
- this.$watch(
37
- () => {
38
- return this.app.meta.locale.current;
39
- },
40
- async () => {
41
- await this.$refetchQueries({ queryKey: ['retrieveMenus'] });
42
- },
43
- );
44
35
  }
45
36
 
46
37
  protected __dispose__() {
@@ -51,7 +42,7 @@ export class ModelMenu extends BeanModelBase {
51
42
 
52
43
  retrieveMenus() {
53
44
  return this.$useStateData({
54
- queryKey: ['retrieveMenus'],
45
+ queryKey: this._getQueryKeyRetrieveMenus(),
55
46
  queryFn: async () => {
56
47
  const data = await this.$api.homeBaseMenu.retrieveMenus({
57
48
  params: { publicPath: this.sys.env.APP_PUBLIC_PATH },
@@ -65,7 +56,7 @@ export class ModelMenu extends BeanModelBase {
65
56
  return item;
66
57
  })
67
58
  ?.filter(item => {
68
- return !item.external || this.$router.checkPathValid(item.link);
59
+ return item.external || !item.link || this.$router.checkPathValid(item.link);
69
60
  });
70
61
  return { ...data, menus };
71
62
  },
@@ -75,12 +66,27 @@ export class ModelMenu extends BeanModelBase {
75
66
  findMenuItem(search: { name?: string; link?: string }): ApiSchemaAMenuDtoMenuItem | undefined {
76
67
  const menus = this.retrieveMenus().data;
77
68
  if (!menus || !menus.menus) return;
78
- return menus.menus.find(
79
- item => (item.name && search.name && item.name === search.name) || item.link === search.link,
80
- );
69
+ const hasName = search.name !== undefined;
70
+ const hasLink = search.link !== undefined;
71
+ if (!hasName && !hasLink) return;
72
+ return menus.menus.find(item => {
73
+ return (hasName && item.name === search.name) || (hasLink && item.link === search.link);
74
+ });
75
+ }
76
+
77
+ private _refetchRetrieveMenus(): Promise<void> {
78
+ return this.$refetchQueries({ queryKey: this._getQueryKeyRetrieveMenus() });
79
+ }
80
+
81
+ private _getQueryKeyRetrieveMenus(): [string, string | undefined, string | undefined] {
82
+ return ['retrieveMenus', this.sys.env.APP_PUBLIC_PATH, this.app.meta.locale.current];
81
83
  }
82
84
 
83
- private _prepareMenuTree(menus: ApiSchemaAMenuDtoMenus, groupName?: string): TypeMenuTree {
85
+ private _prepareMenuTree(
86
+ menus: ApiSchemaAMenuDtoMenus,
87
+ groupName?: string,
88
+ groupNames?: Set<string>,
89
+ ): TypeMenuTree {
84
90
  let children: TypeMenuItem[] = [];
85
91
  if (menus.menus) {
86
92
  children = children.concat(
@@ -99,15 +105,23 @@ export class ModelMenu extends BeanModelBase {
99
105
  const groups = menus.groups
100
106
  .filter(
101
107
  item =>
102
- item.group === groupName ||
103
- (Array.isArray(item.group) && item.group.includes(groupName!)),
108
+ !groupNames?.has(item.name) &&
109
+ (item.group === groupName ||
110
+ (Array.isArray(item.group) && item.group.includes(groupName!))),
104
111
  )
105
112
  .map(menuGroup => {
113
+ const children = this._prepareMenuTree(
114
+ menus,
115
+ menuGroup.name,
116
+ new Set([...(groupNames || []), menuGroup.name]),
117
+ );
118
+ if (children.length === 0) return undefined;
106
119
  return Object.assign({}, menuGroup, {
107
120
  folder: true,
108
- children: this._prepareMenuTree(menus, menuGroup.name),
121
+ children,
109
122
  });
110
- });
123
+ })
124
+ .filter(group => !!group);
111
125
  children = children.concat(groups);
112
126
  }
113
127
  return children.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
@@ -20,14 +20,13 @@ export class RenderTabs extends BeanRenderBase {
20
20
  </div>
21
21
  );
22
22
  if (!$$modelTabs.cache) return domWrapper;
23
-
24
23
  return <ClientOnly>{domWrapper}</ClientOnly>;
25
24
  }
26
25
 
27
26
  private _renderTab(tab: RouteTab): VNode {
28
27
  const $$modelTabs = this.$$modelTabs;
29
28
  const { tabKey, info } = tab;
30
- const titleLocale = this.$text(info?.title || '');
29
+ const titleLocale = info?.title || '';
31
30
  const tabIcon = this.getTabIcon(tab);
32
31
  if (info.folder) {
33
32
  return (
@@ -39,16 +38,18 @@ export class RenderTabs extends BeanRenderBase {
39
38
  </summary>
40
39
  <ClientOnly>
41
40
  <ul class="bg-base-100 rounded-t-none p-2 w-48">
42
- {info.children?.map(item => (
43
- <li key={item.link} onClick={closeNearestDetails}>
44
- <ZItemLink
45
- title={item.title!}
46
- icon={(item.icon as any) ?? $iconName('::none')}
47
- href={item.link && item.external ? item.link : undefined}
48
- to={item.link && !item.external ? item.link : undefined}
49
- ></ZItemLink>
50
- </li>
51
- ))}
41
+ {info.children
42
+ ?.filter(item => !item.folder)
43
+ .map(item => (
44
+ <li key={item.link} onClick={closeNearestDetails}>
45
+ <ZItemLink
46
+ title={item.title!}
47
+ icon={(item.icon as any) ?? $iconName('::none')}
48
+ href={item.link && item.external ? item.link : undefined}
49
+ to={item.link && !item.external ? item.link : undefined}
50
+ ></ZItemLink>
51
+ </li>
52
+ ))}
52
53
  </ul>
53
54
  </ClientOnly>
54
55
  </details>
@@ -28,19 +28,10 @@ export class ModelMenu extends BeanModelBase {
28
28
  // event
29
29
  if (process.env.CLIENT && this.sys.config.ssr.hmr) {
30
30
  this._eventSsrHmrReload = this.sys.meta.event.on('a-ssrhmr:reload', async (_data, next) => {
31
- await this.$refetchQueries({ queryKey: ['retrieveMenus'] });
31
+ await this._refetchRetrieveMenus();
32
32
  return next();
33
33
  });
34
34
  }
35
- // locale
36
- this.$watch(
37
- () => {
38
- return this.app.meta.locale.current;
39
- },
40
- async () => {
41
- await this.$refetchQueries({ queryKey: ['retrieveMenus'] });
42
- },
43
- );
44
35
  }
45
36
 
46
37
  protected __dispose__() {
@@ -51,7 +42,7 @@ export class ModelMenu extends BeanModelBase {
51
42
 
52
43
  retrieveMenus() {
53
44
  return this.$useStateData({
54
- queryKey: ['retrieveMenus'],
45
+ queryKey: this._getQueryKeyRetrieveMenus(),
55
46
  queryFn: async () => {
56
47
  const data = await this.$api.homeBaseMenu.retrieveMenus({
57
48
  params: { publicPath: this.sys.env.APP_PUBLIC_PATH },
@@ -65,7 +56,7 @@ export class ModelMenu extends BeanModelBase {
65
56
  return item;
66
57
  })
67
58
  ?.filter(item => {
68
- return !item.external || this.$router.checkPathValid(item.link);
59
+ return item.external || !item.link || this.$router.checkPathValid(item.link);
69
60
  });
70
61
  return { ...data, menus };
71
62
  },
@@ -75,12 +66,27 @@ export class ModelMenu extends BeanModelBase {
75
66
  findMenuItem(search: { name?: string; link?: string }): ApiSchemaAMenuDtoMenuItem | undefined {
76
67
  const menus = this.retrieveMenus().data;
77
68
  if (!menus || !menus.menus) return;
78
- return menus.menus.find(
79
- item => (item.name && search.name && item.name === search.name) || item.link === search.link,
80
- );
69
+ const hasName = search.name !== undefined;
70
+ const hasLink = search.link !== undefined;
71
+ if (!hasName && !hasLink) return;
72
+ return menus.menus.find(item => {
73
+ return (hasName && item.name === search.name) || (hasLink && item.link === search.link);
74
+ });
75
+ }
76
+
77
+ private _refetchRetrieveMenus(): Promise<void> {
78
+ return this.$refetchQueries({ queryKey: this._getQueryKeyRetrieveMenus() });
79
+ }
80
+
81
+ private _getQueryKeyRetrieveMenus(): [string, string | undefined, string | undefined] {
82
+ return ['retrieveMenus', this.sys.env.APP_PUBLIC_PATH, this.app.meta.locale.current];
81
83
  }
82
84
 
83
- private _prepareMenuTree(menus: ApiSchemaAMenuDtoMenus, groupName?: string): TypeMenuTree {
85
+ private _prepareMenuTree(
86
+ menus: ApiSchemaAMenuDtoMenus,
87
+ groupName?: string,
88
+ groupNames?: Set<string>,
89
+ ): TypeMenuTree {
84
90
  let children: TypeMenuItem[] = [];
85
91
  if (menus.menus) {
86
92
  children = children.concat(
@@ -99,15 +105,23 @@ export class ModelMenu extends BeanModelBase {
99
105
  const groups = menus.groups
100
106
  .filter(
101
107
  item =>
102
- item.group === groupName ||
103
- (Array.isArray(item.group) && item.group.includes(groupName!)),
108
+ !groupNames?.has(item.name) &&
109
+ (item.group === groupName ||
110
+ (Array.isArray(item.group) && item.group.includes(groupName!))),
104
111
  )
105
112
  .map(menuGroup => {
113
+ const children = this._prepareMenuTree(
114
+ menus,
115
+ menuGroup.name,
116
+ new Set([...(groupNames || []), menuGroup.name]),
117
+ );
118
+ if (children.length === 0) return undefined;
106
119
  return Object.assign({}, menuGroup, {
107
120
  folder: true,
108
- children: this._prepareMenuTree(menus, menuGroup.name),
121
+ children,
109
122
  });
110
- });
123
+ })
124
+ .filter(group => !!group);
111
125
  children = children.concat(groups);
112
126
  }
113
127
  return children.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));