ProjectManagement.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <template>
  2. <div class="model-management">
  3. <!-- 页面头部 -->
  4. <PageHeader />
  5. <!-- 主要内容区域 -->
  6. <main class="main-content">
  7. <!-- 左侧垂直菜单 -->
  8. <aside class="left-sidebar">
  9. <el-menu :model-value="activeMenu" class="el-menu-vertical-demo" background-color="#f8f9fa" text-color="#303133" active-text-color="#409eff" style="height: 100%; margin: 0;" @open="handleOpen" @close="handleClose" @select="(key) => handleMenuSelect(key)">
  10. <el-menu-item-group>
  11. <template #title><span style="font-size: 1.45rem; font-weight: 600; color: #303133;">项目分类</span></template>
  12. <!-- 数字孪生分类 -->
  13. <el-sub-menu index="1">
  14. <template #title>
  15. <el-icon><Menu /></el-icon>
  16. <span>数字孪生</span>
  17. </template>
  18. <el-menu-item index="1-1">数字孪生太浦河</el-menu-item>
  19. <el-menu-item index="1-2">数字孪生水文站</el-menu-item>
  20. <el-menu-item index="1-3">数字孪生灌区</el-menu-item>
  21. </el-sub-menu>
  22. <!-- 水利信息化分类 -->
  23. <el-sub-menu index="2">
  24. <template #title>
  25. <el-icon><Menu /></el-icon>
  26. <span>水利信息化</span>
  27. </template>
  28. <el-menu-item index="2-1">流域数据采集</el-menu-item>
  29. </el-sub-menu>
  30. </el-menu-item-group>
  31. </el-menu>
  32. </aside>
  33. <!-- 右侧内容区域 -->
  34. <section class="right-content">
  35. <!-- 面包屑导航已移除 -->
  36. <component :is="activeComponent" ref="shuiliGongchengRef" />
  37. </section>
  38. </main>
  39. </div>
  40. </template>
  41. <script setup>
  42. import PageHeader from './PageHeader.vue'
  43. import { ref, shallowRef, markRaw } from 'vue'
  44. import { Menu, Setting, House } from '@element-plus/icons-vue'
  45. import ShuiliGongcheng from './content/ShuiliGongcheng.vue'
  46. import JidianShebei from './content/JidianShebei.vue'
  47. import ShuiliSheshi from './content/ShuiliSheshi.vue'
  48. // 创建一个可响应的引用,用于访问ShuiliGongcheng组件的实例
  49. const shuiliGongchengRef = ref(null)
  50. // 使用markRaw标记组件,避免Vue将其转换为响应式对象
  51. const ShuiliGongchengRaw = markRaw(ShuiliGongcheng)
  52. const JidianShebeiRaw = markRaw(JidianShebei)
  53. const ShuiliSheshiRaw = markRaw(ShuiliSheshi)
  54. const activeMenu = ref('1')
  55. const activeMenuName = ref('数字孪生')
  56. const activeComponent = ref(ShuiliGongchengRaw)
  57. const handleOpen = (key, keyPath) => {
  58. console.log('Menu opened:', key, keyPath)
  59. if (key === '1') {
  60. activeMenu.value = key
  61. activeMenuName.value = '数字孪生'
  62. activeComponent.value = ShuiliGongchengRaw
  63. console.log('Set active component to ShuiliGongcheng')
  64. // 当打开数字孪生大类时,显示所有数字孪生项目
  65. if (shuiliGongchengRef.value) {
  66. shuiliGongchengRef.value.selectCategory('数字孪生')
  67. }
  68. } else if (key === '2') {
  69. activeMenu.value = key
  70. activeMenuName.value = '水利信息化'
  71. activeComponent.value = ShuiliGongchengRaw
  72. console.log('Set active component to ShuiliGongcheng')
  73. // 当打开水利信息化大类时,显示所有水利信息化项目
  74. if (shuiliGongchengRef.value) {
  75. shuiliGongchengRef.value.selectCategory('水利信息化')
  76. }
  77. }
  78. }
  79. const handleClose = (key, keyPath) => {
  80. console.log(key, keyPath)
  81. }
  82. const handleMenuSelect = (key) => {
  83. console.log('Menu selected:', key)
  84. activeMenu.value = key
  85. // 根据选择的菜单项更新活动菜单名称和组件
  86. if (key === '1') {
  87. activeMenuName.value = '数字孪生'
  88. activeComponent.value = ShuiliGongchengRaw
  89. // 当选择数字孪生大类时,显示所有数字孪生项目
  90. if (shuiliGongchengRef.value) {
  91. shuiliGongchengRef.value.selectCategory('数字孪生')
  92. }
  93. } else if (key === '1-1') {
  94. activeMenuName.value = '数字孪生太浦河'
  95. activeComponent.value = ShuiliGongchengRaw
  96. // 当选择数字孪生太浦河子类时,显示对应的项目
  97. if (shuiliGongchengRef.value) {
  98. shuiliGongchengRef.value.selectCategory('数字孪生太浦河')
  99. }
  100. } else if (key === '1-2') {
  101. activeMenuName.value = '数字孪生水文站'
  102. activeComponent.value = ShuiliGongchengRaw
  103. // 当选择数字孪生水文站子类时,显示对应的项目
  104. if (shuiliGongchengRef.value) {
  105. shuiliGongchengRef.value.selectCategory('数字孪生水文站')
  106. }
  107. } else if (key === '1-3') {
  108. activeMenuName.value = '数字孪生灌区'
  109. activeComponent.value = ShuiliGongchengRaw
  110. // 当选择数字孪生灌区子类时,显示对应的项目
  111. if (shuiliGongchengRef.value) {
  112. shuiliGongchengRef.value.selectCategory('数字孪生灌区')
  113. }
  114. } else if (key === '2') {
  115. activeMenuName.value = '水利信息化'
  116. activeComponent.value = ShuiliGongchengRaw
  117. // 当选择水利信息化大类时,显示所有水利信息化项目
  118. if (shuiliGongchengRef.value) {
  119. shuiliGongchengRef.value.selectCategory('水利信息化')
  120. }
  121. } else if (key === '2-1') {
  122. activeMenuName.value = '流域数据采集'
  123. activeComponent.value = ShuiliGongchengRaw
  124. // 当选择流域数据采集子类时,显示对应的项目
  125. if (shuiliGongchengRef.value) {
  126. shuiliGongchengRef.value.selectCategory('流域数据采集')
  127. }
  128. } else {
  129. activeMenuName.value = '数字孪生'
  130. activeComponent.value = ShuiliGongchengRaw
  131. // 默认显示所有数字孪生项目
  132. if (shuiliGongchengRef.value) {
  133. shuiliGongchengRef.value.selectCategory('数字孪生')
  134. }
  135. }
  136. console.log('Active component:', activeComponent.value)
  137. }
  138. </script>
  139. <style scoped>
  140. .model-management {
  141. width: 100%;
  142. height: 100%;
  143. display: flex;
  144. flex-direction: column;
  145. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  146. overflow: hidden;
  147. background: #f8f9fa;
  148. }
  149. /* 主要内容区域 */
  150. .main-content {
  151. flex: 1;
  152. width: 100%;
  153. padding: 0;
  154. overflow-y: auto;
  155. height: 100%;
  156. display: flex;
  157. }
  158. /* 左侧垂直菜单 */
  159. .left-sidebar {
  160. width: 280px;
  161. background: #f8f9fa;
  162. box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
  163. height: 100%;
  164. border-radius: 0;
  165. margin: 0;
  166. overflow: hidden;
  167. }
  168. .el-menu-vertical-demo {
  169. border-right: none;
  170. font-size: 1rem !important;
  171. }
  172. .el-menu-vertical-demo .el-menu-item-group__title {
  173. font-weight: 600;
  174. color: #303133 !important;
  175. line-height: 1.5;
  176. }
  177. .el-menu-vertical-demo .el-sub-menu .el-menu-item {
  178. color: #909399 !important;
  179. font-size: 0.875rem !important;
  180. line-height: 1.5;
  181. }
  182. /* 选中状态的样式 */
  183. .el-menu-vertical-demo .el-menu-item.is-active,
  184. .el-menu-vertical-demo .el-sub-menu.is-active > .el-sub-menu__title {
  185. color: #409eff !important;
  186. font-weight: 600 !important;
  187. }
  188. /* 大类选中时的样式增强 - 匹配所有可能的激活状态 */
  189. .el-menu-vertical-demo .el-sub-menu.is-active > .el-sub-menu__title,
  190. .el-menu-vertical-demo .el-sub-menu.is-opened > .el-sub-menu__title,
  191. .el-menu-vertical-demo .el-sub-menu[class*="is-active"] > .el-sub-menu__title,
  192. .el-menu-vertical-demo .el-sub-menu[class*="is-opened"] > .el-sub-menu__title,
  193. .el-menu-vertical-demo .el-sub-menu:has(.is-active) > .el-sub-menu__title,
  194. .el-menu-vertical-demo .el-sub-menu:has(.is-opened) > .el-sub-menu__title {
  195. background-color: rgba(64, 158, 255, 0.25) !important;
  196. border-left: 4px solid #409eff !important;
  197. box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2) !important;
  198. color: #409eff !important;
  199. font-weight: 600 !important;
  200. padding-left: 19px !important;
  201. transition: all 0.3s ease !important;
  202. }
  203. /* 确保激活状态样式优先级更高 */
  204. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu.is-active > .el-sub-menu__title,
  205. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu.is-opened > .el-sub-menu__title,
  206. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu:has(.is-active) > .el-sub-menu__title,
  207. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu:has(.is-opened) > .el-sub-menu__title {
  208. background-color: rgba(64, 158, 255, 0.3) !important;
  209. border-left: 4px solid #409eff !important;
  210. box-shadow: 0 4px 16px rgba(64, 158, 255, 0.25) !important;
  211. font-weight: 700 !important;
  212. }
  213. /* 直接点击大类标题时的样式 */
  214. .el-menu-vertical-demo .el-sub-menu > .el-sub-menu__title:hover,
  215. .el-menu-vertical-demo .el-sub-menu > .el-sub-menu__title:focus {
  216. background-color: rgba(64, 158, 255, 0.2) !important;
  217. border-left: 4px solid #409eff !important;
  218. color: #409eff !important;
  219. font-weight: 600 !important;
  220. }
  221. /* 为了确保样式能被正确应用,添加更直接的选择器 */
  222. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu > .el-sub-menu__title {
  223. position: relative;
  224. z-index: 10;
  225. }
  226. /* 当子菜单被选中时,父级大类也显示高亮 */
  227. .el-menu-vertical-demo .el-sub-menu .el-menu-item.is-active {
  228. background-color: rgba(64, 158, 255, 0.1) !important;
  229. color: #409eff !important;
  230. font-weight: 600 !important;
  231. }
  232. /* 大类鼠标悬停效果 */
  233. .el-menu-vertical-demo .el-sub-menu > .el-sub-menu__title:hover {
  234. background-color: rgba(64, 158, 255, 0.15) !important;
  235. border-left: 3px solid rgba(64, 158, 255, 0.8) !important;
  236. transition: all 0.3s ease !important;
  237. }
  238. /* 直接为大类标题添加更通用的样式,确保选中时有视觉反馈 */
  239. .el-menu-vertical-demo > .el-menu-item-group > .el-sub-menu > .el-sub-menu__title {
  240. padding: 12px 20px !important;
  241. margin: 5px 0 !important;
  242. border-radius: 4px !important;
  243. transition: all 0.3s ease !important;
  244. }
  245. /* 增强选中状态的样式,使用更通用的选择器 */
  246. .el-menu-vertical-demo .el-sub-menu.is-active > .el-sub-menu__title {
  247. background-color: rgba(64, 158, 255, 0.25) !important;
  248. border-left: 4px solid #409eff !important;
  249. color: #409eff !important;
  250. font-weight: 600 !important;
  251. padding-left: 19px !important;
  252. }
  253. .menu-title-text {
  254. font-size: 1rem !important;
  255. font-weight: 600;
  256. }
  257. .tree-menu {
  258. padding: 1.5rem;
  259. height: 100%;
  260. display: flex;
  261. flex-direction: column;
  262. border-radius: 12px;
  263. background-color: #f8f9fa;
  264. }
  265. .menu-title {
  266. font-size: 1.125rem;
  267. color: #2c3e50;
  268. margin-bottom: 1.5rem;
  269. font-weight: 600;
  270. line-height: 1.5;
  271. }
  272. .menu-items {
  273. display: flex;
  274. flex-direction: column;
  275. gap: 0;
  276. flex-grow: 1;
  277. }
  278. .menu-item {
  279. display: flex;
  280. align-items: center;
  281. gap: 0.8rem;
  282. padding: 1rem 1.5rem;
  283. cursor: pointer;
  284. transition: all 0.3s ease;
  285. font-size: 1rem;
  286. color: #577cf3;
  287. font-weight: 400;
  288. line-height: 1.6;
  289. }
  290. .menu-item:hover {
  291. background: #f0f5ff;
  292. }
  293. .menu-icon {
  294. font-size: 1.25rem;
  295. }
  296. /* 右侧内容区域 */
  297. .right-content {
  298. flex: 1;
  299. padding: 1rem;
  300. overflow-y: auto;
  301. }
  302. .model-section {
  303. padding: 0 1rem;
  304. margin-bottom: 2.5rem;
  305. }
  306. .section-title {
  307. font-size: 1.5rem;
  308. color: #2c3e50;
  309. margin-bottom: 2rem;
  310. font-weight: 600;
  311. line-height: 1.4;
  312. }
  313. .model-grid {
  314. display: grid;
  315. grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
  316. gap: 2rem;
  317. }
  318. .model-card {
  319. background: white;
  320. padding: 2.5rem;
  321. border-radius: 16px;
  322. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  323. transition: all 0.3s ease;
  324. }
  325. .model-card:hover {
  326. transform: translateY(-10px);
  327. box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
  328. }
  329. .model-name {
  330. font-size: 1.25rem;
  331. color: #2c3e50;
  332. margin-bottom: 1rem;
  333. font-weight: 600;
  334. line-height: 1.4;
  335. }
  336. .model-description {
  337. font-size: 1rem;
  338. color: #7f8c8d;
  339. line-height: 1.6;
  340. margin-bottom: 2rem;
  341. font-weight: 400;
  342. }
  343. .model-actions {
  344. display: flex;
  345. gap: 1rem;
  346. }
  347. .btn {
  348. padding: 0.8rem 1.5rem;
  349. border: none;
  350. border-radius: 50px;
  351. font-size: 1rem;
  352. font-weight: 400;
  353. cursor: pointer;
  354. transition: all 0.3s ease;
  355. text-decoration: none;
  356. display: inline-block;
  357. white-space: nowrap;
  358. line-height: 1.6;
  359. }
  360. .btn-primary {
  361. background: #577cf3;
  362. color: white;
  363. }
  364. .btn-primary:hover {
  365. transform: translateY(-3px);
  366. box-shadow: 0 15px 30px rgba(87, 124, 243, 0.4);
  367. }
  368. .btn-secondary {
  369. background: white;
  370. color: #577cf3;
  371. border: 2px solid #577cf3;
  372. }
  373. .btn-secondary:hover {
  374. background: #577cf3;
  375. color: white;
  376. transform: translateY(-3px);
  377. }
  378. /* 页面底部 */
  379. .page-footer {
  380. background: white;
  381. color: #577cf3;
  382. text-align: center;
  383. padding: 1.5rem 0;
  384. height: 10px;
  385. display: flex;
  386. align-items: center;
  387. justify-content: center;
  388. border-top: 1px solid #f0f5ff;
  389. }
  390. .menu-bar {
  391. height: 100%;
  392. background: #f0f5ff;
  393. }
  394. .page-footer p {
  395. margin: 0;
  396. font-size: 0.875rem;
  397. line-height: 1.5;
  398. font-weight: 400;
  399. }
  400. </style>