diff --git a/admin/src/layout.tsx b/admin/src/layout.tsx
index 23bec33..4908346 100644
--- a/admin/src/layout.tsx
+++ b/admin/src/layout.tsx
@@ -13,6 +13,11 @@ import {
IconUser,
IconMenuFold,
IconMenuUnfold,
+ IconIdcard,
+ IconCompass,
+ IconRobot,
+ IconScan,
+ IconUserGroup,
} from '@arco-design/web-react/icon';
import { useSelector } from 'react-redux';
import qs from 'query-string';
@@ -37,6 +42,18 @@ function getIconFromKey(key) {
switch (key) {
case 'dashboard':
return ;
+ case 'card':
+ return ;
+ case 'crm':
+ return ;
+ case 'ai':
+ return ;
+ case 'nfc':
+ return ;
+ case 'team':
+ return ;
+ case 'settings-biz':
+ return ;
case 'list':
return ;
case 'form':
diff --git a/admin/src/routes.ts b/admin/src/routes.ts
index 0ba4b4b..d3d8072 100644
--- a/admin/src/routes.ts
+++ b/admin/src/routes.ts
@@ -92,6 +92,7 @@ export const routes: IRoute[] = [
{
name: 'menu.visualization',
key: 'visualization',
+ ignore: true,
children: [
{
name: 'menu.visualization.dataAnalysis',
@@ -120,6 +121,7 @@ export const routes: IRoute[] = [
{
name: 'menu.list',
key: 'list',
+ ignore: true,
children: [
{
name: 'menu.list.searchTable',
@@ -134,6 +136,7 @@ export const routes: IRoute[] = [
{
name: 'menu.form',
key: 'form',
+ ignore: true,
children: [
{
name: 'menu.form.group',
@@ -154,6 +157,7 @@ export const routes: IRoute[] = [
{
name: 'menu.profile',
key: 'profile',
+ ignore: true,
children: [
{
name: 'menu.profile.basic',
@@ -165,6 +169,7 @@ export const routes: IRoute[] = [
{
name: 'menu.result',
key: 'result',
+ ignore: true,
children: [
{
name: 'menu.result.success',
@@ -181,6 +186,7 @@ export const routes: IRoute[] = [
{
name: 'menu.exception',
key: 'exception',
+ ignore: true,
children: [
{
name: 'menu.exception.403',
diff --git a/admin/tests/routes-menu.test.ts b/admin/tests/routes-menu.test.ts
new file mode 100644
index 0000000..0adb1c8
--- /dev/null
+++ b/admin/tests/routes-menu.test.ts
@@ -0,0 +1,70 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { routes } from '../src/routes.ts';
+
+const adminRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
+const layoutPath = path.join(adminRoot, 'src/layout.tsx');
+
+const CORE_BIZ_MODULES = [
+ 'dashboard',
+ 'card',
+ 'crm',
+ 'ai',
+ 'nfc',
+ 'team',
+ 'settings-biz',
+ 'user',
+];
+
+const UNRELATED_DEMO_MODULES = [
+ 'visualization',
+ 'list',
+ 'form',
+ 'profile',
+ 'result',
+ 'exception',
+];
+
+test('1. 核心出海业务菜单必须保留在侧边栏正常展示 (ignore !== true)', () => {
+ for (const key of CORE_BIZ_MODULES) {
+ const route = routes.find((item) => item.key === key);
+ assert.ok(route, `routes.ts 必须存在核心业务模块: ${key}`);
+ assert.notEqual(
+ route.ignore,
+ true,
+ `核心业务模块 ${key} 不应被 ignore,必须展示在菜单中`
+ );
+ }
+});
+
+test('2. 与出海业务无关的模板演示 Demo 菜单必须配置 ignore: true 进行隐藏', () => {
+ for (const key of UNRELATED_DEMO_MODULES) {
+ const route = routes.find((item) => item.key === key);
+ if (route) {
+ assert.equal(
+ route.ignore,
+ true,
+ `与需求无关的模板模块 ${key} 必须配置 ignore: true 隐藏`
+ );
+ }
+ }
+});
+
+test('3. layout.tsx 必须为核心业务模块配置对应的 Arco 矢量图标', () => {
+ assert.ok(fs.existsSync(layoutPath), '必须存在 layout.tsx');
+ const content = fs.readFileSync(layoutPath, 'utf-8');
+
+ // 必须映射业务模块图标
+ assert.ok(content.includes("'card'"), "layout.tsx 必须为 'card' 模块配置图标");
+ assert.ok(content.includes("'crm'"), "layout.tsx 必须为 'crm' 模块配置图标");
+ assert.ok(content.includes("'ai'"), "layout.tsx 必须为 'ai' 模块配置图标");
+ assert.ok(content.includes("'nfc'"), "layout.tsx 必须为 'nfc' 模块配置图标");
+ assert.ok(content.includes("'team'"), "layout.tsx 必须为 'team' 模块配置图标");
+ assert.ok(
+ content.includes("'settings-biz'"),
+ "layout.tsx 必须为 'settings-biz' 模块配置图标"
+ );
+});