seed-menus.ts 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. // prisma/mongo/seed-admin.ts
  2. import { PrismaClient, MenuType, AdType } from '@prisma/mongo/client';
  3. import type { Prisma } from '@prisma/mongo/client';
  4. const prisma = new PrismaClient();
  5. // =============================================================================
  6. // MENU SEEDS DATA
  7. // =============================================================================
  8. interface SeedMenu {
  9. legacyId: number;
  10. legacyParentId: number | null;
  11. title: string;
  12. type: MenuType;
  13. name: string;
  14. path: string;
  15. icon: string | null;
  16. componentKey?: string | null;
  17. order: number;
  18. meta?: Prisma.JsonValue;
  19. }
  20. const MENU_SEEDS: SeedMenu[] = [
  21. // ======================
  22. // 营销中心 (Marketing)
  23. // ======================
  24. {
  25. legacyId: 1,
  26. legacyParentId: null,
  27. title: '营销中心',
  28. type: 'DIRECTORY',
  29. name: 'Marketing Management',
  30. path: '/marketing',
  31. icon: 'i-carbon:application-web',
  32. order: 100,
  33. meta: {
  34. title: '营销中心',
  35. i18n: 'route.general.root',
  36. icon: 'i-carbon:application-web',
  37. },
  38. },
  39. {
  40. legacyId: 2,
  41. legacyParentId: 1,
  42. title: '视频管理',
  43. type: 'MENU',
  44. name: 'videoList',
  45. path: '/marketing/video',
  46. icon: 'i-carbon:video',
  47. order: 1,
  48. meta: {
  49. title: '视频管理',
  50. i18n: 'route.general.video.root',
  51. icon: 'i-carbon:video',
  52. },
  53. },
  54. {
  55. legacyId: 3,
  56. legacyParentId: 2,
  57. title: '视频列表',
  58. type: 'SUBMENU',
  59. name: 'videoList',
  60. path: '/marketing/videoList',
  61. icon: null,
  62. componentKey: '@/views/marketing_center/video_mgnt/list.vue',
  63. order: 1,
  64. meta: {
  65. title: '视频列表',
  66. i18n: 'route.general.video.list',
  67. sidebar: false,
  68. breadcrumb: false,
  69. cache: ['videoCreate', 'videoEdit'],
  70. },
  71. },
  72. {
  73. legacyId: 4,
  74. legacyParentId: 2,
  75. title: '新增视频',
  76. type: 'SUBMENU',
  77. name: 'videoCreate',
  78. path: '/marketing/video/detail',
  79. icon: null,
  80. componentKey: '@/views/marketing_center/video_mgnt/detail.vue',
  81. order: 2,
  82. meta: {
  83. title: '新增视频',
  84. i18n: 'route.general.video.create',
  85. sidebar: false,
  86. activeMenu: '/marketing/videoList',
  87. cache: true,
  88. noCache: 'videoList',
  89. },
  90. },
  91. {
  92. legacyId: 5,
  93. legacyParentId: 2,
  94. title: '编辑视频',
  95. type: 'SUBMENU',
  96. name: 'videoEdit',
  97. path: '/marketing/video/detail/:id',
  98. icon: null,
  99. componentKey: '@/views/marketing_center/video_mgnt/detail.vue',
  100. order: 3,
  101. meta: {
  102. title: '编辑视频',
  103. i18n: 'route.general.video.edit',
  104. sidebar: false,
  105. activeMenu: '/marketing/videoList',
  106. cache: true,
  107. noCache: 'videoList',
  108. },
  109. },
  110. {
  111. legacyId: 6,
  112. legacyParentId: 1,
  113. title: '分类管理',
  114. type: 'MENU',
  115. name: 'categoryList',
  116. path: '/marketing/category',
  117. icon: 'i-carbon:category',
  118. order: 2,
  119. meta: {
  120. title: '分类管理',
  121. i18n: 'route.general.category.root',
  122. icon: 'i-carbon:category',
  123. },
  124. },
  125. {
  126. legacyId: 7,
  127. legacyParentId: 6,
  128. title: '分类列表',
  129. type: 'SUBMENU',
  130. name: 'categoryList',
  131. path: '/marketing/categoryList',
  132. icon: null,
  133. componentKey: '@/views/marketing_center/category_mgnt/list.vue',
  134. order: 1,
  135. meta: {
  136. title: '分类列表',
  137. i18n: 'route.general.category.list',
  138. sidebar: false,
  139. breadcrumb: false,
  140. cache: ['categoryCreate', 'categoryEdit'],
  141. },
  142. },
  143. {
  144. legacyId: 8,
  145. legacyParentId: 6,
  146. title: '新增分类',
  147. type: 'SUBMENU',
  148. name: 'categoryCreate',
  149. path: '/marketing/category/detail',
  150. icon: null,
  151. componentKey: '@/views/marketing_center/category_mgnt/detail.vue',
  152. order: 2,
  153. meta: {
  154. title: '新增分类',
  155. i18n: 'route.general.category.create',
  156. sidebar: false,
  157. activeMenu: '/marketing/categoryList',
  158. cache: true,
  159. noCache: 'categoryList',
  160. },
  161. },
  162. {
  163. legacyId: 9,
  164. legacyParentId: 6,
  165. title: '编辑分类',
  166. type: 'SUBMENU',
  167. name: 'categoryEdit',
  168. path: '/marketing/category/detail/:id',
  169. icon: null,
  170. componentKey: '@/views/marketing_center/category_mgnt/detail.vue',
  171. order: 3,
  172. meta: {
  173. title: '编辑分类',
  174. i18n: 'route.general.category.edit',
  175. sidebar: false,
  176. activeMenu: '/marketing/categoryList',
  177. cache: true,
  178. noCache: 'categoryList',
  179. },
  180. },
  181. {
  182. legacyId: 10,
  183. legacyParentId: 1,
  184. title: '标签管理',
  185. type: 'MENU',
  186. name: 'tagList',
  187. path: '/marketing/tag',
  188. icon: 'i-carbon:tag',
  189. order: 3,
  190. meta: {
  191. title: '标签管理',
  192. i18n: 'route.general.tag.root',
  193. icon: 'i-carbon:tag',
  194. },
  195. },
  196. {
  197. legacyId: 11,
  198. legacyParentId: 10,
  199. title: '标签列表',
  200. type: 'SUBMENU',
  201. name: 'tagList',
  202. path: '/marketing/tagList',
  203. icon: null,
  204. componentKey: '@/views/marketing_center/tag_mgnt/list.vue',
  205. order: 1,
  206. meta: {
  207. title: '标签列表',
  208. i18n: 'route.general.tag.list',
  209. sidebar: false,
  210. breadcrumb: false,
  211. cache: ['tagCreate', 'tagEdit'],
  212. },
  213. },
  214. {
  215. legacyId: 12,
  216. legacyParentId: 10,
  217. title: '新增标签',
  218. type: 'SUBMENU',
  219. name: 'tagCreate',
  220. path: '/marketing/tag/detail',
  221. icon: null,
  222. componentKey: '@/views/marketing_center/tag_mgnt/detail.vue',
  223. order: 2,
  224. meta: {
  225. title: '新增标签',
  226. i18n: 'route.general.tag.create',
  227. sidebar: false,
  228. activeMenu: '/marketing/tagList',
  229. cache: true,
  230. noCache: 'tagList',
  231. },
  232. },
  233. {
  234. legacyId: 13,
  235. legacyParentId: 10,
  236. title: '编辑标签',
  237. type: 'SUBMENU',
  238. name: 'tagEdit',
  239. path: '/marketing/tag/detail/:id',
  240. icon: null,
  241. componentKey: '@/views/marketing_center/tag_mgnt/detail.vue',
  242. order: 3,
  243. meta: {
  244. title: '编辑标签',
  245. i18n: 'route.general.tag.edit',
  246. sidebar: false,
  247. activeMenu: '/marketing/tagList',
  248. cache: true,
  249. noCache: 'tagList',
  250. },
  251. },
  252. {
  253. legacyId: 14,
  254. legacyParentId: 1,
  255. title: '广告管理',
  256. type: 'MENU',
  257. name: 'adsList',
  258. path: '/marketing/ads',
  259. icon: 'i-carbon:image-copy',
  260. order: 4,
  261. meta: {
  262. title: '广告管理',
  263. i18n: 'route.general.ads.root',
  264. icon: 'i-carbon:image-copy',
  265. },
  266. },
  267. {
  268. legacyId: 15,
  269. legacyParentId: 14,
  270. title: '广告列表',
  271. type: 'SUBMENU',
  272. name: 'adsList',
  273. path: '/marketing/adsList',
  274. icon: null,
  275. componentKey: '@/views/marketing_center/ads_mgnt/list.vue',
  276. order: 1,
  277. meta: {
  278. title: '广告列表',
  279. i18n: 'route.general.ads.list',
  280. sidebar: false,
  281. breadcrumb: false,
  282. cache: ['adsCreate', 'adsEdit'],
  283. },
  284. },
  285. {
  286. legacyId: 16,
  287. legacyParentId: 14,
  288. title: '新增广告',
  289. type: 'SUBMENU',
  290. name: 'adsCreate',
  291. path: '/marketing/ads/detail',
  292. icon: null,
  293. componentKey: '@/views/marketing_center/ads_mgnt/detail.vue',
  294. order: 2,
  295. meta: {
  296. title: '新增广告',
  297. i18n: 'route.general.ads.create',
  298. sidebar: false,
  299. activeMenu: '/marketing/adsList',
  300. cache: true,
  301. noCache: 'adsList',
  302. },
  303. },
  304. {
  305. legacyId: 17,
  306. legacyParentId: 14,
  307. title: '编辑广告',
  308. type: 'SUBMENU',
  309. name: 'adsEdit',
  310. path: '/marketing/ads/detail/:id',
  311. icon: null,
  312. componentKey: '@/views/marketing_center/ads_mgnt/detail.vue',
  313. order: 3,
  314. meta: {
  315. title: '编辑广告',
  316. i18n: 'route.general.ads.edit',
  317. sidebar: false,
  318. activeMenu: '/marketing/adsList',
  319. cache: true,
  320. noCache: 'adsList',
  321. },
  322. },
  323. {
  324. legacyId: 18,
  325. legacyParentId: 1,
  326. title: '参数管理',
  327. type: 'MENU',
  328. name: 'paramList',
  329. path: '/marketing/param',
  330. icon: 'i-carbon:settings',
  331. order: 5,
  332. meta: {
  333. title: '参数管理',
  334. i18n: 'route.general.param.root',
  335. icon: 'i-carbon:settings',
  336. },
  337. },
  338. {
  339. legacyId: 19,
  340. legacyParentId: 18,
  341. title: '参数列表',
  342. type: 'SUBMENU',
  343. name: 'paramList',
  344. path: '/marketing/paramList',
  345. icon: null,
  346. componentKey: '@/views/marketing_center/param_mgnt/list.vue',
  347. order: 1,
  348. meta: {
  349. title: '参数列表',
  350. i18n: 'route.general.param.list',
  351. sidebar: false,
  352. breadcrumb: false,
  353. cache: ['paramCreate', 'paramEdit'],
  354. },
  355. },
  356. {
  357. legacyId: 20,
  358. legacyParentId: 18,
  359. title: '新增参数',
  360. type: 'SUBMENU',
  361. name: 'paramCreate',
  362. path: '/marketing/param/detail',
  363. icon: null,
  364. componentKey: '@/views/marketing_center/param_mgnt/detail.vue',
  365. order: 2,
  366. meta: {
  367. title: '新增参数',
  368. i18n: 'route.general.param.create',
  369. sidebar: false,
  370. activeMenu: '/marketing/paramList',
  371. cache: true,
  372. noCache: 'paramList',
  373. },
  374. },
  375. {
  376. legacyId: 21,
  377. legacyParentId: 18,
  378. title: '编辑参数',
  379. type: 'SUBMENU',
  380. name: 'paramEdit',
  381. path: '/marketing/param/detail/:id',
  382. icon: null,
  383. componentKey: '@/views/marketing_center/param_mgnt/detail.vue',
  384. order: 3,
  385. meta: {
  386. title: '编辑参数',
  387. i18n: 'route.general.param.edit',
  388. sidebar: false,
  389. activeMenu: '/marketing/paramList',
  390. cache: true,
  391. noCache: 'paramList',
  392. },
  393. },
  394. {
  395. legacyId: 22,
  396. legacyParentId: 1,
  397. title: '渠道管理',
  398. type: 'MENU',
  399. name: 'channelList',
  400. path: '/marketing/channel',
  401. icon: 'i-carbon:network-3',
  402. order: 6,
  403. meta: {
  404. title: '渠道管理',
  405. i18n: 'route.general.channel.root',
  406. icon: 'i-carbon:network-3',
  407. },
  408. },
  409. {
  410. legacyId: 23,
  411. legacyParentId: 22,
  412. title: '渠道列表',
  413. type: 'SUBMENU',
  414. name: 'channelList',
  415. path: '/marketing/channelList',
  416. icon: null,
  417. componentKey: '@/views/marketing_center/channel_mgnt/list.vue',
  418. order: 1,
  419. meta: {
  420. title: '渠道列表',
  421. i18n: 'route.general.channel.list',
  422. sidebar: false,
  423. breadcrumb: false,
  424. cache: ['channelCreate', 'channelEdit'],
  425. },
  426. },
  427. {
  428. legacyId: 24,
  429. legacyParentId: 22,
  430. title: '新增渠道',
  431. type: 'SUBMENU',
  432. name: 'channelCreate',
  433. path: '/marketing/channel/detail',
  434. icon: null,
  435. componentKey: '@/views/marketing_center/channel_mgnt/detail.vue',
  436. order: 2,
  437. meta: {
  438. title: '新增渠道',
  439. i18n: 'route.general.channel.create',
  440. sidebar: false,
  441. activeMenu: '/marketing/channelList',
  442. cache: true,
  443. noCache: 'channelList',
  444. },
  445. },
  446. {
  447. legacyId: 25,
  448. legacyParentId: 22,
  449. title: '编辑渠道',
  450. type: 'SUBMENU',
  451. name: 'channelEdit',
  452. path: '/marketing/channel/detail/:id',
  453. icon: null,
  454. componentKey: '@/views/marketing_center/channel_mgnt/detail.vue',
  455. order: 3,
  456. meta: {
  457. title: '编辑渠道',
  458. i18n: 'route.general.channel.edit',
  459. sidebar: false,
  460. activeMenu: '/marketing/channelList',
  461. cache: true,
  462. noCache: 'channelList',
  463. },
  464. },
  465. // ======================
  466. // 数据中心 (Data Center)
  467. // ======================
  468. {
  469. legacyId: 26,
  470. legacyParentId: null,
  471. title: '数据中心',
  472. type: 'DIRECTORY',
  473. name: 'Data Center',
  474. path: '/datacenter',
  475. icon: 'i-carbon:data-vis-4',
  476. order: 200,
  477. meta: {
  478. title: '数据中心',
  479. i18n: 'route.general.root',
  480. icon: 'i-carbon:data-vis-4',
  481. },
  482. },
  483. {
  484. legacyId: 27,
  485. legacyParentId: 26,
  486. title: 'APP访问记录',
  487. type: 'MENU',
  488. name: 'appAccessList',
  489. path: '/datacenter/appAccessRecord',
  490. icon: 'i-carbon:mobile',
  491. order: 1,
  492. meta: {
  493. title: 'APP访问记录',
  494. i18n: 'route.general.appAccessRecord.root',
  495. icon: 'i-carbon:mobile',
  496. },
  497. },
  498. {
  499. legacyId: 28,
  500. legacyParentId: 27,
  501. title: 'APP访问记录',
  502. type: 'SUBMENU',
  503. name: 'appAccessList',
  504. path: '/datacenter/appAccessList',
  505. icon: null,
  506. componentKey: '@/views/data_center/appAccess_records/list.vue',
  507. order: 1,
  508. meta: {
  509. title: 'APP访问记录',
  510. i18n: 'route.general.appAccessRecord.list',
  511. sidebar: false,
  512. breadcrumb: false,
  513. cache: ['appAccessRecordCreate', 'appAccessRecordEdit'],
  514. },
  515. },
  516. {
  517. legacyId: 29,
  518. legacyParentId: 26,
  519. title: '广告点击记录',
  520. type: 'MENU',
  521. name: 'adsAccessList',
  522. path: '/datacenter/adsAccessRecord',
  523. icon: 'i-carbon:touch-1',
  524. order: 2,
  525. meta: {
  526. title: '广告点击记录',
  527. i18n: 'route.general.adsAccessRecord.root',
  528. icon: 'i-carbon:touch-1',
  529. },
  530. },
  531. {
  532. legacyId: 30,
  533. legacyParentId: 29,
  534. title: '广告点击记录',
  535. type: 'SUBMENU',
  536. name: 'adsAccessList',
  537. path: '/datacenter/adsAccessList',
  538. icon: null,
  539. componentKey: '@/views/data_center/adsAccess_records/list.vue',
  540. order: 1,
  541. meta: {
  542. title: '广告点击记录',
  543. i18n: 'route.general.category.list',
  544. sidebar: false,
  545. breadcrumb: false,
  546. cache: ['adsAccessRecordCreate', 'adsAccessRecordEdit'],
  547. },
  548. },
  549. // ======================
  550. // 统计中心 (Stats Center)
  551. // ======================
  552. {
  553. legacyId: 31,
  554. legacyParentId: null,
  555. title: '统计中心',
  556. type: 'DIRECTORY',
  557. name: 'Statistics Management',
  558. path: '/stats',
  559. icon: 'i-carbon:chart-line',
  560. order: 300,
  561. meta: {
  562. title: '统计中心',
  563. i18n: 'route.general.root',
  564. icon: 'i-carbon:chart-line',
  565. },
  566. },
  567. {
  568. legacyId: 32,
  569. legacyParentId: 31,
  570. title: '每日统计',
  571. type: 'MENU',
  572. name: 'dailyStats',
  573. path: '/stats/dailyStats',
  574. icon: 'i-carbon:calendar',
  575. order: 1,
  576. meta: {
  577. title: '每日统计',
  578. i18n: 'route.general.dailyStats.root',
  579. icon: 'i-carbon:calendar',
  580. },
  581. },
  582. {
  583. legacyId: 33,
  584. legacyParentId: 32,
  585. title: '每日统计',
  586. type: 'SUBMENU',
  587. name: 'dailyStatsList',
  588. path: '/stats/dailyStatsList',
  589. icon: null,
  590. componentKey: '@/views/stats_center/daily_stats/list.vue',
  591. order: 1,
  592. meta: {
  593. title: '每日统计',
  594. i18n: 'route.general.video.list',
  595. sidebar: false,
  596. breadcrumb: false,
  597. cache: ['videoCreate', 'videoEdit'],
  598. },
  599. },
  600. {
  601. legacyId: 34,
  602. legacyParentId: 31,
  603. title: '广告统计',
  604. type: 'MENU',
  605. name: 'adsStatsList',
  606. path: '/stats/adsStats',
  607. icon: 'i-carbon:chart-bar',
  608. order: 2,
  609. meta: {
  610. title: '广告统计',
  611. i18n: 'route.general.adsStats.root',
  612. icon: 'i-carbon:chart-bar',
  613. },
  614. },
  615. {
  616. legacyId: 35,
  617. legacyParentId: 34,
  618. title: '广告统计',
  619. type: 'SUBMENU',
  620. name: 'adsStatsList',
  621. path: '/stats/adsStatsList',
  622. icon: null,
  623. componentKey: '@/views/stats_center/ads_stats/list.vue',
  624. order: 1,
  625. meta: {
  626. title: '广告统计',
  627. i18n: 'route.general.adsStats.list',
  628. sidebar: false,
  629. breadcrumb: false,
  630. cache: ['adsStatsCreate', 'adsStatsEdit'],
  631. },
  632. },
  633. {
  634. legacyId: 36,
  635. legacyParentId: 31,
  636. title: '广告汇总',
  637. type: 'MENU',
  638. name: 'adsStatsSummary',
  639. path: '/stats/adsStatsSummary',
  640. icon: 'i-carbon:chart-pie',
  641. componentKey: '@/views/stats_center/ads_stats_summary/list.vue',
  642. order: 3,
  643. meta: {
  644. title: '广告汇总',
  645. i18n: 'route.general.adsStatsSummary.root',
  646. icon: 'i-carbon:chart-pie',
  647. },
  648. },
  649. {
  650. legacyId: 37,
  651. legacyParentId: 36,
  652. title: '广告汇总',
  653. type: 'SUBMENU',
  654. name: 'adsStatsSummaryList',
  655. path: '/stats/adsStatsSummaryList',
  656. icon: null,
  657. componentKey: '@/views/stats_center/ads_stats_summary/list.vue',
  658. order: 1,
  659. meta: {
  660. title: '广告汇总',
  661. i18n: 'route.general.adsStatsSummary.list',
  662. sidebar: false,
  663. breadcrumb: false,
  664. cache: ['adsStatsSummaryCreate', 'adsStatsSummaryEdit'],
  665. },
  666. },
  667. {
  668. legacyId: 38,
  669. legacyParentId: 31,
  670. title: '渠道统计',
  671. type: 'MENU',
  672. name: 'channelStatsList',
  673. path: '/stats/channelStats',
  674. icon: 'i-carbon:flow',
  675. order: 4,
  676. meta: {
  677. title: '渠道统计',
  678. i18n: 'route.general.channelStats.root',
  679. icon: 'i-carbon:flow',
  680. },
  681. },
  682. {
  683. legacyId: 39,
  684. legacyParentId: 38,
  685. title: '渠道统计',
  686. type: 'SUBMENU',
  687. name: 'channelStatsList',
  688. path: '/stats/channelStatsList',
  689. icon: null,
  690. componentKey: '@/views/stats_center/channel_stats/list.vue',
  691. order: 1,
  692. meta: {
  693. title: '渠道统计',
  694. i18n: 'route.general.channel.list',
  695. sidebar: false,
  696. breadcrumb: false,
  697. cache: ['channelCreate', 'channelEdit'],
  698. },
  699. },
  700. {
  701. legacyId: 40,
  702. legacyParentId: 31,
  703. title: '渠道汇总',
  704. type: 'MENU',
  705. name: 'channelStatsSummaryList',
  706. path: '/stats/channelStatsSummary',
  707. icon: 'i-carbon:report',
  708. order: 5,
  709. meta: {
  710. title: '渠道汇总',
  711. i18n: 'route.general.channelStatsSummary.root',
  712. icon: 'i-carbon:report',
  713. },
  714. },
  715. {
  716. legacyId: 41,
  717. legacyParentId: 40,
  718. title: '渠道汇总',
  719. type: 'SUBMENU',
  720. name: 'channelStatsSummaryList',
  721. path: '/stats/channelStatsSummaryList',
  722. icon: null,
  723. componentKey: '@/views/stats_center/channel_stats_summary/list.vue',
  724. order: 1,
  725. meta: {
  726. title: '参数列表',
  727. i18n: 'route.general.param.list',
  728. sidebar: false,
  729. breadcrumb: false,
  730. cache: ['channelStatsSummaryCreate', 'channelStatsSummaryEdit'],
  731. },
  732. },
  733. // ======================
  734. // 系统管理 (System)
  735. // ======================
  736. {
  737. legacyId: 42,
  738. legacyParentId: null,
  739. title: '账号管理',
  740. type: 'DIRECTORY',
  741. name: 'Sytem Management',
  742. path: '/system',
  743. icon: 'i-carbon:user-admin',
  744. order: 400,
  745. meta: {
  746. title: '账号管理',
  747. i18n: 'route.general.root',
  748. icon: 'i-carbon:user-admin',
  749. },
  750. },
  751. {
  752. legacyId: 43,
  753. legacyParentId: 42,
  754. title: '账号列表',
  755. type: 'MENU',
  756. name: 'userList',
  757. path: '/system/users',
  758. icon: 'i-carbon:user-multiple',
  759. componentKey: '@/views/system_mgnt/users/list.vue',
  760. order: 1,
  761. meta: {
  762. title: '账号列表',
  763. i18n: 'route.general.manager.root',
  764. icon: 'i-carbon:user-multiple',
  765. },
  766. },
  767. {
  768. legacyId: 44,
  769. legacyParentId: 42,
  770. title: '角色列表',
  771. type: 'MENU',
  772. name: 'roleList',
  773. path: '/system/roles',
  774. icon: 'i-carbon:user-role',
  775. order: 2,
  776. meta: {
  777. title: '角色列表',
  778. i18n: 'route.general.role.root',
  779. icon: 'i-carbon:user-role',
  780. },
  781. },
  782. {
  783. legacyId: 45,
  784. legacyParentId: 44,
  785. title: '角色列表',
  786. type: 'SUBMENU',
  787. name: 'roleList',
  788. path: '/system/roleList',
  789. icon: null,
  790. componentKey: '@/views/system_mgnt/roles/list.vue',
  791. order: 1,
  792. meta: {
  793. title: '角色列表',
  794. i18n: 'route.general.role.list',
  795. sidebar: false,
  796. breadcrumb: false,
  797. cache: ['roleCreate', 'roleEdit'],
  798. },
  799. },
  800. {
  801. legacyId: 46,
  802. legacyParentId: 44,
  803. title: '新增角色',
  804. type: 'SUBMENU',
  805. name: 'roleCreate',
  806. path: '/system/roles/detail',
  807. icon: null,
  808. componentKey: '@/views/system_mgnt/roles/detail.vue',
  809. order: 2,
  810. meta: {
  811. title: '新增角色',
  812. i18n: 'route.general.role.create',
  813. sidebar: false,
  814. activeMenu: '/system/roleList',
  815. cache: true,
  816. noCache: 'roleList',
  817. },
  818. },
  819. {
  820. legacyId: 47,
  821. legacyParentId: 44,
  822. title: '编辑角色',
  823. type: 'SUBMENU',
  824. name: 'roleEdit',
  825. path: '/system/roles/detail/:id',
  826. icon: null,
  827. componentKey: '@/views/system_mgnt/roles/detail.vue',
  828. order: 3,
  829. meta: {
  830. title: '编辑角色',
  831. i18n: 'route.general.role.edit',
  832. sidebar: false,
  833. activeMenu: '/system/roleList',
  834. cache: true,
  835. noCache: 'roleList',
  836. },
  837. },
  838. {
  839. legacyId: 48,
  840. legacyParentId: 42,
  841. title: '权限列表',
  842. type: 'MENU',
  843. name: 'menus',
  844. path: '/system/menus',
  845. icon: 'i-carbon:list-boxes',
  846. order: 3,
  847. meta: {
  848. title: '权限列表',
  849. i18n: 'route.general.menu.root',
  850. icon: 'i-carbon:list-boxes',
  851. },
  852. },
  853. {
  854. legacyId: 49,
  855. legacyParentId: 48,
  856. title: '菜单列表',
  857. type: 'SUBMENU',
  858. name: 'menuList',
  859. path: '/system/menuList',
  860. icon: null,
  861. componentKey: '@/views/system_mgnt/menus/list.vue',
  862. order: 1,
  863. meta: {
  864. title: '菜单列表',
  865. i18n: 'route.general.menu.list',
  866. sidebar: false,
  867. breadcrumb: false,
  868. cache: ['menuCreate', 'menuEdit'],
  869. },
  870. },
  871. {
  872. legacyId: 50,
  873. legacyParentId: 48,
  874. title: '新增菜单',
  875. type: 'SUBMENU',
  876. name: 'menuCreate',
  877. path: '/system/menus/detail',
  878. icon: null,
  879. componentKey: '@/views/system_mgnt/menus/detail.vue',
  880. order: 2,
  881. meta: {
  882. title: '新增菜单',
  883. i18n: 'route.general.menu.create',
  884. sidebar: false,
  885. activeMenu: '/system/menuList',
  886. cache: true,
  887. noCache: 'menuList',
  888. },
  889. },
  890. {
  891. legacyId: 51,
  892. legacyParentId: 48,
  893. title: '编辑菜单',
  894. type: 'SUBMENU',
  895. name: 'menuEdit',
  896. path: '/system/menus/detail/:id',
  897. icon: null,
  898. componentKey: '@/views/system_mgnt/menus/detail.vue',
  899. order: 3,
  900. meta: {
  901. title: '编辑菜单',
  902. i18n: 'route.general.menu.edit',
  903. sidebar: false,
  904. activeMenu: '/system/menuList',
  905. cache: true,
  906. noCache: 'menuList',
  907. },
  908. },
  909. {
  910. legacyId: 52,
  911. legacyParentId: 42,
  912. title: 'Redis监控',
  913. type: 'MENU',
  914. name: 'redisMonitor',
  915. path: '/system/redisMonitor',
  916. icon: 'i-carbon:list-boxes',
  917. order: 4,
  918. meta: {
  919. title: 'Redis监控',
  920. i18n: 'route.general.redisMonitor.list',
  921. icon: 'i-carbon:list-boxes',
  922. },
  923. },
  924. {
  925. legacyId: 53,
  926. legacyParentId: 52,
  927. title: 'Redis列表',
  928. type: 'SUBMENU',
  929. name: 'redisList',
  930. path: '/system/redisList',
  931. icon: null,
  932. componentKey: '@/views/system_mgnt/redisMonitor/list.vue',
  933. order: 1,
  934. meta: {
  935. title: 'Redis列表',
  936. i18n: 'route.general.redisMonitor.list',
  937. sidebar: false,
  938. breadcrumb: false,
  939. cache: ['redisCreate', 'redisEdit'],
  940. },
  941. },
  942. ];
  943. // =============================================================================
  944. // HELPER FUNCTIONS
  945. // =============================================================================
  946. /**
  947. * Get default action permissions based on menu type
  948. */
  949. function getDefaultPermissions(type: MenuType) {
  950. switch (type) {
  951. case 'DIRECTORY':
  952. return { canView: 1, canCreate: 0, canUpdate: 0, canDelete: 0 };
  953. case 'MENU':
  954. case 'SUBMENU':
  955. return { canView: 1, canCreate: 1, canUpdate: 1, canDelete: 1 };
  956. case 'BUTTON':
  957. return { canView: 0, canCreate: 1, canUpdate: 0, canDelete: 0 };
  958. default:
  959. return { canView: 0, canCreate: 0, canUpdate: 0, canDelete: 0 };
  960. }
  961. }
  962. /**
  963. * Seed menus with multi-pass insertion to handle parent-child relationships
  964. */
  965. async function seedMenus() {
  966. console.log('📝 Seeding menus...');
  967. await prisma.$runCommandRaw({
  968. delete: 'sys_api_permission',
  969. deletes: [{ q: {}, limit: 0 }],
  970. });
  971. await prisma.$runCommandRaw({
  972. delete: 'sys_menu',
  973. deletes: [{ q: {}, limit: 0 }],
  974. });
  975. const legacyIdToNewId = new Map<number, string>();
  976. const inserted = new Set<number>();
  977. // Multi-pass insertion: insert records whose parent is either null or already inserted
  978. while (inserted.size < MENU_SEEDS.length) {
  979. let progress = false;
  980. for (const seed of MENU_SEEDS) {
  981. if (inserted.has(seed.legacyId)) {
  982. continue;
  983. }
  984. // If has a parent, but parent not inserted yet → skip this round
  985. if (
  986. seed.legacyParentId !== null &&
  987. !legacyIdToNewId.has(seed.legacyParentId)
  988. ) {
  989. continue;
  990. }
  991. const parentId =
  992. seed.legacyParentId === null
  993. ? null
  994. : legacyIdToNewId.get(seed.legacyParentId)!;
  995. const permissions = getDefaultPermissions(seed.type);
  996. const menuCreateData: Prisma.SysMenuUncheckedCreateInput = {
  997. parentId,
  998. title: seed.title,
  999. status: true,
  1000. type: seed.type,
  1001. order: seed.order,
  1002. frontendAuth: seed.path,
  1003. path: seed.path,
  1004. name: seed.name,
  1005. icon: seed.icon,
  1006. redirect: null,
  1007. componentKey: seed.componentKey ?? null,
  1008. meta: seed.meta ?? undefined,
  1009. ...permissions,
  1010. };
  1011. const created = await prisma.sysMenu.upsert({
  1012. // Use unique index on frontendAuth to identify menu
  1013. where: { frontendAuth: seed.path },
  1014. update: {
  1015. parentId,
  1016. title: seed.title,
  1017. status: true,
  1018. type: seed.type,
  1019. order: seed.order,
  1020. // keep frontendAuth as path
  1021. path: seed.path,
  1022. name: seed.name,
  1023. icon: seed.icon,
  1024. redirect: null,
  1025. componentKey: seed.componentKey ?? null,
  1026. // meta undefined means do not overwrite; if provided, update
  1027. ...(seed.meta !== undefined
  1028. ? { meta: seed.meta as Prisma.InputJsonValue }
  1029. : {}),
  1030. ...permissions,
  1031. },
  1032. create: {
  1033. ...menuCreateData,
  1034. },
  1035. });
  1036. legacyIdToNewId.set(seed.legacyId, created.id);
  1037. inserted.add(seed.legacyId);
  1038. progress = true;
  1039. console.log(
  1040. ` ✓ Menu [${seed.type.padEnd(9)}] ${seed.name.padEnd(25)} → ID: ${created.id}`,
  1041. );
  1042. }
  1043. if (!progress) {
  1044. // Nothing could be inserted in this pass → some parentId is invalid or cyclic
  1045. const pending = MENU_SEEDS.filter((s) => !inserted.has(s.legacyId)).map(
  1046. (s) => ({
  1047. legacyId: s.legacyId,
  1048. legacyParentId: s.legacyParentId,
  1049. name: s.name,
  1050. }),
  1051. );
  1052. console.error('❌ Could not resolve parents for menus:', pending);
  1053. throw new Error(
  1054. 'Menu seeding aborted: unresolved parent relationships. Check legacyParentId values.',
  1055. );
  1056. }
  1057. }
  1058. console.log('✅ Menus seeded successfully');
  1059. console.log(` - Total menus: ${inserted.size}`);
  1060. }
  1061. // =============================================================================
  1062. // MAIN EXECUTION
  1063. // =============================================================================
  1064. async function main() {
  1065. console.log('🌱 Starting menus seeding...\n');
  1066. try {
  1067. console.log('seeding menus...');
  1068. await seedMenus();
  1069. console.log('');
  1070. console.log('\n🎉 Menus seeding completed successfully!');
  1071. } catch (error) {
  1072. console.error('\n❌ Error during seeding:', error);
  1073. throw error;
  1074. }
  1075. }
  1076. main()
  1077. .then(async () => {
  1078. await prisma.$disconnect();
  1079. })
  1080. .catch(async (e) => {
  1081. console.error(e);
  1082. await prisma.$disconnect();
  1083. process.exit(1);
  1084. });